From 9601f44ac8c33d4c8b9d4534539616e151da62e4 Mon Sep 17 00:00:00 2001 From: Edward Date: Wed, 4 Sep 2019 12:12:27 +1000 Subject: [PATCH] Return the plugin-apis to this repo (#1900) * Return plugin-api to the main repo * Spotless * Migrate all external plugin-api references to the project in this repo * Add licence header * Update repo reference for publish, even if commented * Use real configuration for publishing plugin-api This was tested with the `:plugins:publishMavenJavaPublicationToMavenLocal` task and checking the local Maven repo to make sure it was using the correct paths Signed-off-by: Edward Evans Signed-off-by: Adrian Sutton --- acceptance-tests/build.gradle | 2 +- build.gradle | 3 + consensus/common/build.gradle | 2 +- crypto/build.gradle | 2 +- ethereum/core/build.gradle | 2 +- ethereum/mock-p2p/build.gradle | 2 +- ethereum/rlp/build.gradle | 2 +- gradle/versions.gradle | 2 - metrics/core/build.gradle | 3 +- metrics/rocksdb/build.gradle | 3 +- pantheon/build.gradle | 2 +- plugins/build.gradle | 78 +++++++++ .../pantheon/plugin/PantheonContext.java | 42 +++++ .../pantheon/plugin/PantheonPlugin.java | 53 ++++++ .../pegasys/pantheon/plugin/Unstable.java | 28 ++++ .../pegasys/pantheon/plugin/data/Address.java | 19 +++ .../pantheon/plugin/data/BinaryData.java | 46 ++++++ .../pantheon/plugin/data/BlockHeader.java | 156 ++++++++++++++++++ .../pegasys/pantheon/plugin/data/Hash.java | 23 +++ .../pegasys/pantheon/plugin/data/Log.java | 43 +++++ .../pantheon/plugin/data/Quantity.java | 35 ++++ .../pantheon/plugin/data/Transaction.java | 130 +++++++++++++++ .../pantheon/plugin/data/UnformattedData.java | 19 +++ .../plugin/services/MetricsSystem.java | 119 +++++++++++++ .../services/PantheonConfiguration.java | 36 ++++ .../plugin/services/PantheonEvents.java | 117 +++++++++++++ .../plugin/services/PicoCLIOptions.java | 37 +++++ .../plugin/services/StorageService.java | 38 +++++ .../services/exception/StorageException.java | 47 ++++++ .../plugin/services/metrics/Counter.java | 30 ++++ .../services/metrics/LabelledMetric.java | 31 ++++ .../services/metrics/MetricCategory.java | 43 +++++ .../metrics/MetricCategoryRegistry.java | 29 ++++ .../services/metrics/OperationTimer.java | 43 +++++ .../services/storage/KeyValueStorage.java | 78 +++++++++ .../storage/KeyValueStorageFactory.java | 57 +++++++ .../storage/KeyValueStorageTransaction.java | 49 ++++++ .../services/storage/SegmentIdentifier.java | 30 ++++ services/kvstore/build.gradle | 3 +- services/pipeline/build.gradle | 2 +- services/tasks/build.gradle | 3 +- settings.gradle | 1 + util/build.gradle | 2 +- 43 files changed, 1474 insertions(+), 18 deletions(-) create mode 100644 plugins/build.gradle create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonContext.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonPlugin.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/Unstable.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Address.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BinaryData.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BlockHeader.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Hash.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Log.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Quantity.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Transaction.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/data/UnformattedData.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/MetricsSystem.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonConfiguration.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonEvents.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PicoCLIOptions.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/StorageService.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/exception/StorageException.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/Counter.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/LabelledMetric.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategory.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategoryRegistry.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/OperationTimer.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorage.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageFactory.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageTransaction.java create mode 100644 plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/SegmentIdentifier.java diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index a44614ef43..4f96ac73da 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -33,6 +33,7 @@ dependencies { testImplementation project(':metrics:core') testImplementation project(':pantheon') testImplementation project(path: ':pantheon', configuration: 'testArtifacts') + testImplementation project(':plugins') testImplementation project(':services:kvstore') testImplementation project(':testutil') testImplementation project(':util') @@ -55,7 +56,6 @@ dependencies { testImplementation 'tech.pegasys.ethsigner.internal:core' testImplementation 'tech.pegasys.ethsigner.internal:file-based' testImplementation 'tech.pegasys.ethsigner.internal:signing-api' - testImplementation 'tech.pegasys.pantheon:plugin-api' } test.enabled = false diff --git a/build.gradle b/build.gradle index 65393efa04..5505ec5682 100644 --- a/build.gradle +++ b/build.gradle @@ -272,6 +272,9 @@ allprojects { task deploy() {} +tasks.register('checkPluginAPIChanges', DefaultTask) { } +checkPluginAPIChanges.dependsOn(':plugins:checkAPIChanges') +check.dependsOn('checkPluginAPIChanges') subprojects { diff --git a/consensus/common/build.gradle b/consensus/common/build.gradle index 6ee28b7228..2c8eefb706 100644 --- a/consensus/common/build.gradle +++ b/consensus/common/build.gradle @@ -26,7 +26,7 @@ jar { } dependencies { - api 'tech.pegasys.pantheon:plugin-api' + api project(':plugins') implementation project(':ethereum:core') implementation project(':ethereum:jsonrpc') diff --git a/crypto/build.gradle b/crypto/build.gradle index f5c049cc39..fcd259bfbe 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -26,10 +26,10 @@ jar { } dependencies { + api project(':plugins') api project(':util') api 'org.bouncycastle:bcprov-jdk15on' - api 'tech.pegasys.pantheon:plugin-api' implementation 'com.google.guava:guava' implementation 'org.apache.logging.log4j:log4j-api' diff --git a/ethereum/core/build.gradle b/ethereum/core/build.gradle index c6372dd153..e2969a4fd8 100644 --- a/ethereum/core/build.gradle +++ b/ethereum/core/build.gradle @@ -32,13 +32,13 @@ dependencies { implementation project(':ethereum:rlp') implementation project(':ethereum:trie') implementation project(':metrics:core') + implementation project(':plugins') implementation project(':services:kvstore') implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'com.google.guava:guava' implementation 'io.vertx:vertx-core' implementation 'org.apache.logging.log4j:log4j-api' - implementation 'tech.pegasys.pantheon:plugin-api' runtime 'org.apache.logging.log4j:log4j-core' diff --git a/ethereum/mock-p2p/build.gradle b/ethereum/mock-p2p/build.gradle index 34bdf0f099..33333da44f 100644 --- a/ethereum/mock-p2p/build.gradle +++ b/ethereum/mock-p2p/build.gradle @@ -26,7 +26,7 @@ jar { } dependencies { - api 'tech.pegasys.pantheon:plugin-api' + api project(':plugins') implementation project(':ethereum:p2p') implementation project(':ethereum:permissioning') diff --git a/ethereum/rlp/build.gradle b/ethereum/rlp/build.gradle index ee7646d553..966dd846a5 100644 --- a/ethereum/rlp/build.gradle +++ b/ethereum/rlp/build.gradle @@ -26,8 +26,8 @@ jar { } dependencies { + api project(':plugins') api project(':util') - api 'tech.pegasys.pantheon:plugin-api' implementation 'com.google.guava:guava' implementation 'io.vertx:vertx-core' diff --git a/gradle/versions.gradle b/gradle/versions.gradle index b204614819..a4bc24d517 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -95,7 +95,5 @@ dependencyManagement { dependency "tech.pegasys.ethsigner.internal:core:0.3.0" dependency "tech.pegasys.ethsigner.internal:file-based:0.3.0" dependency "tech.pegasys.ethsigner.internal:signing-api:0.3.0" - - dependency "tech.pegasys.pantheon:plugin-api:1.2.3" } } diff --git a/metrics/core/build.gradle b/metrics/core/build.gradle index 75eb9fef8e..c6d0efe91b 100644 --- a/metrics/core/build.gradle +++ b/metrics/core/build.gradle @@ -33,6 +33,8 @@ publishing { dependencies { + implementation project(':plugins') + implementation 'com.google.guava:guava' implementation 'io.prometheus:simpleclient' implementation 'io.prometheus:simpleclient_common' @@ -41,7 +43,6 @@ dependencies { implementation 'io.vertx:vertx-core' implementation 'io.vertx:vertx-web' implementation 'org.apache.logging.log4j:log4j-api' - implementation 'tech.pegasys.pantheon:plugin-api' runtime 'org.apache.logging.log4j:log4j-core' diff --git a/metrics/rocksdb/build.gradle b/metrics/rocksdb/build.gradle index 6139c05907..80287125f6 100644 --- a/metrics/rocksdb/build.gradle +++ b/metrics/rocksdb/build.gradle @@ -27,12 +27,11 @@ jar { dependencies { implementation project(':metrics:core') + implementation project(':plugins') implementation project(':services:util') implementation 'com.google.guava:guava' implementation 'io.prometheus:simpleclient' implementation 'org.apache.logging.log4j:log4j-api' implementation 'org.rocksdb:rocksdbjni' - - implementation 'tech.pegasys.pantheon:plugin-api' } diff --git a/pantheon/build.gradle b/pantheon/build.gradle index d267eb60c0..64cb95efdc 100644 --- a/pantheon/build.gradle +++ b/pantheon/build.gradle @@ -44,6 +44,7 @@ dependencies { implementation project(':ethereum:rlp') implementation project(':metrics:core') implementation project(':nat') + implementation project(':plugins') implementation project(':services:kvstore') implementation 'com.fasterxml.jackson.core:jackson-databind' @@ -56,7 +57,6 @@ dependencies { implementation 'net.consensys.cava:cava-toml' implementation 'org.apache.logging.log4j:log4j-api' implementation 'org.springframework.security:spring-security-crypto' - implementation 'tech.pegasys.pantheon:plugin-api' runtime 'org.apache.logging.log4j:log4j-core' runtime 'org.apache.logging.log4j:log4j-slf4j-impl' diff --git a/plugins/build.gradle b/plugins/build.gradle new file mode 100644 index 0000000000..bea3381af4 --- /dev/null +++ b/plugins/build.gradle @@ -0,0 +1,78 @@ +/* + * 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. + */ + +import net.ltgt.gradle.errorprone.CheckSeverity +import java.security.MessageDigest + +apply plugin: 'java-library' +jar { + baseName 'plugin-api' + manifest { + attributes( + 'Specification-Title': baseName, + 'Specification-Version': project.version, + 'Implementation-Title': baseName, + 'Implementation-Version': calculateVersion() + ) + } +} + + +dependencies { +} + +configurations { testArtifacts } +artifacts { testSupportArtifacts testSupportJar } + +class FileStateChecker extends DefaultTask { + Set files + String knownHash + + @TaskAction + def CheckState() { + def digestor = MessageDigest.getInstance("SHA-256") + + this.files.sort().each { + digestor.update(it.readBytes()) + } + def currentHash = digestor.digest()encodeBase64().toString() + if (this.knownHash != currentHash) { + throw new GradleException("""For the Plugin APIs the checksum of the project did not match what was expected. + +If this is a deliberate change where you have thought through backwards compatibility, +then update "Expected" for "Calculated" in the appropriate build.gradle as the knownHash for this task. +Expected : ${this.knownHash} +Calculated : ${currentHash} +""") + } + } +} + +tasks.register('checkAPIChanges', FileStateChecker) { + description = "Checks that the API for the Plugin-API project does not change without deliberate thought" + files = sourceSets.main.allJava.files + knownHash = 'PBo0D4R6/1EYXEn+k0nmWHW4TkklUWQbQGNqgWzslfw=' +} +check.dependsOn('checkAPIChanges') + +publishing { + publications { + mavenJava(MavenPublication) { + groupId 'tech.pegasys.pantheon.plugin-api' + pom { + name = 'Pantheon Plugins Library' + description = 'Core Plugins Libraries for Pantheon' + } + } + } +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonContext.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonContext.java new file mode 100644 index 0000000000..eb2e6b1d18 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonContext.java @@ -0,0 +1,42 @@ +/* + * 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.plugin; + +import java.util.Optional; + +/** Allows plugins to access Pantheon services. */ +public interface PantheonContext { + + /** + * Get the requested service, if it is available. There are a number of reasons that a service may + * not be available: + * + *
    + *
  • The service may not have started yet. Most services are not available before the {@link + * PantheonPlugin#start()} method is called + *
  • The service is not supported by this version of Pantheon + *
  • The service may not be applicable to the current configuration. For example some services + * may only be available when a proof of authority network is in use + *
+ * + *

Since plugins are automatically loaded, unless the user has specifically requested + * functionality provided by the plugin, no error should be raised if required services are + * unavailable. + * + * @param serviceType the class defining the requested service. + * @param the service type + * @return an optional containing the instance of the requested service, or empty if the service + * is unavailable + */ + Optional getService(Class serviceType); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonPlugin.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonPlugin.java new file mode 100644 index 0000000000..6ff5e32125 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/PantheonPlugin.java @@ -0,0 +1,53 @@ +/* + * 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.plugin; + +/** + * Base interface for Pantheon plugins. + * + *

Plugins are discovered and loaded using {@link java.util.ServiceLoader} from jar files within + * Pantheon's plugin directory. See the {@link java.util.ServiceLoader} documentation for how to + * register plugins. + */ +public interface PantheonPlugin { + + /** + * Called when the plugin is first registered with Pantheon. Plugins are registered very early in + * the Pantheon life-cycle and should use this callback to register any command line options + * required via the PicoCLIOptions service. + * + *

The context parameter should be stored in a field in the plugin. This is the + * only time it will be provided to the plugin and is how the plugin will interact with Pantheon. + * + *

Typically the plugin will not begin operation until the {@link #start()} method is called. + * + * @param context the context that provides access to Pantheon services. + */ + void register(PantheonContext context); + + /** + * Called once Pantheon has loaded configuration and is starting up. The plugin should begin + * operation, including registering any event listener with Pantheon services and starting any + * background threads the plugin requires. + */ + void start(); + + /** + * Called when the plugin is being stopped. This method will be called as part of Pantheon + * shutting down but may also be called at other times to disable the plugin. + * + *

The plugin should remove any registered listeners and stop any background threads it + * started. + */ + void stop(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/Unstable.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/Unstable.java new file mode 100644 index 0000000000..511c71c61c --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/Unstable.java @@ -0,0 +1,28 @@ +/* + * 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.plugin; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import java.lang.annotation.Retention; + +/** + * This annotation is an indicator that the interface or method may evolve in a way that it not + * backwards compatible. Such as deleting methods, changing signatures, and adding checked + * exceptions. Authors are advised to exercise caution when using these APIs. + */ +@Retention(CLASS) +@java.lang.annotation.Target({METHOD, TYPE}) +public @interface Unstable {} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Address.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Address.java new file mode 100644 index 0000000000..3f99319551 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Address.java @@ -0,0 +1,19 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** An interface for {@link BinaryData} that also represents an Ethereum account address. */ +@Unstable +public interface Address extends UnformattedData {} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BinaryData.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BinaryData.java new file mode 100644 index 0000000000..3cc243f863 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BinaryData.java @@ -0,0 +1,46 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** Super class for all types that are ultimately represented by binary data. */ +@Unstable +public interface BinaryData { + + /** + * The byte level representation of the binary data. This array should be treated as read only + * constant data as any changes will not be reflected in the source. + * + * @return a read-only array of the bytes of the binary data. + */ + byte[] getByteArray(); + + /** + * A hex string representation of the data. This hex string will represent the hex of the entire + * binary data and will be "0x" prefixed. APIs that depend on shortend forms will + * need to process the string. + * + * @return A string repsenting the hex encodeing of the data. + */ + String getHexString(); + + /** + * The size, in bytes, of the contained binary data. Because {@link #getByteArray()} may cause the + * underlying data to be copied using this size method is preferred when such a check would avoid + * a call to {@link #getByteArray()} or {@link #getHexString()}. + * + * @return The length of the binary data in bytes. + */ + int size(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BlockHeader.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BlockHeader.java new file mode 100644 index 0000000000..1cf2e71167 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/BlockHeader.java @@ -0,0 +1,156 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** + * The minimum set of data for a BlockHeader, as defined in the Ethereum Yellow Paper. + */ +@Unstable +public interface BlockHeader { + + /** + * The Keccak 256-bit hash of the parent block’s header, in its entirety. + * + * @return The Keccak 256-bit hash of the parent block’s header, in its entirety. + */ + Hash getParentHash(); + + /** + * The Keccak 256-bit hash of the ommers list portion of this block. + * + * @return The Keccak 256-bit hash of the ommers list portion of this block. + */ + Hash getOmmersHash(); + + /** + * The 160-bit address to which all fees collected from the successful mining of this block be + * transferred. + * + *

The name in the yellow paper is beneficiary. + * + * @return The 160-bit address to which all fees collected from the successful mining of this + * block be transferred. + */ + Address getCoinbase(); + + /** + * The Keccak 256-bit hash of the root node of the state trie, after all transactions are executed + * and finalisations applied. + * + * @return The Keccak 256-bit hash of the root node of the state trie, after all transactions are + * executed and finalisations applied. + */ + Hash getStateRoot(); + + /** + * The Keccak 256-bit hash of theroot node of the trie structure populated with each transaction + * in the transactions list portion of the block. + * + * @return The Keccak 256-bit hash of theroot node of the trie structure populated with each + * transaction in the transactions list portion of the block. + */ + Hash getTransactionsRoot(); + + /** + * The Keccak 256-bit hash of the root node of the trie structure populated with the receipts of + * each transaction in the transactions list portion of the block. + * + * @return The Keccak 256-bit hash of the root node of the trie structure populated with the + * receipts of each transaction in the transactions list portion of the block. + */ + Hash getReceiptsRoot(); + + /** + * The Bloom filter composed from indexable information (logger address and log topics) contained + * in each log entry from the receipt of each transaction in the transactions list. + * + * @return The Bloom filter composed from indexable information (logger address and log topics) + * contained in each log entry from the receipt of each transaction in the transactions list. + */ + UnformattedData getLogsBloom(); + + /** + * A scalar value corresponding to the difficulty level of this block. This can be calculated from + * the previous block’s difficulty level and the timestamp. + * + * @return A scalar value corresponding to the difficulty level of this block. This can be + * calculated from the previous block’s difficulty level and the timestamp. + */ + Quantity getDifficulty(); + + /** + * A scalar value equal to the number of ancestor blocks. The genesis block has a number of zero. + * + * @return A scalar value equal to the number of ancestor blocks. The genesis block has a number + * of zero. + */ + long getNumber(); + + /** + * A scalar value equal to the current limit of gas expenditure per block. + * + * @return A scalar value equal to the current limit of gas expenditure per block. + */ + long getGasLimit(); + + /** + * A scalar value equal to the total gas used in transactions in this block. + * + * @return A scalar value equal to the total gas used in transactions in this block. + */ + long getGasUsed(); + + /** + * A scalar value equal to the reasonable output of Unix’s time() at this block’s inception. + * + * @return A scalar value equal to the reasonable output of Unix’s time() at this block’s + * inception. + */ + long getTimestamp(); + + /** + * An arbitrary byte array containing data relevant to this block. This must be 32 bytes or fewer. + * + * @return An arbitrary byte array containing data relevant to this block. This must be 32 bytes + * or fewer. + */ + UnformattedData getExtraData(); + + /** + * A 256-bit hash which, combined with the nonce, proves that a sufficient amount of computation + * has been carried out on this block. + * + * @return A 256-bit hash which, combined with the nonce, proves that a sufficient amount of + * computation has been carried out on this block. + */ + Hash getMixHash(); + + /** + * A 64-bit value which, combined with the mixhash, proves that a sufficient amount of computation + * has been carried out on this block. + * + * @return A 64-bit value which, combined with the mixhash, proves that a sufficient amount of + * computation has been carried out on this block. + */ + long getNonce(); + + /** + * The Keccak 256-bit hash of this header. + * + * @return The Keccak 256-bit hash of this header. + */ + Hash getBlockHash(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Hash.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Hash.java new file mode 100644 index 0000000000..2a6cbc2b8d --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Hash.java @@ -0,0 +1,23 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** + * A marker interface indicating that this {@link UnformattedData} represents a hash of some sort. + * The particular algorithm depends on the source, it may be ripemd, keccak, or some other + * algorithm. + */ +@Unstable +public interface Hash extends UnformattedData {} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Log.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Log.java new file mode 100644 index 0000000000..61f7a942cc --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Log.java @@ -0,0 +1,43 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +import java.util.List; + +/** A Log entry from a transaction execution. */ +@Unstable +public interface Log { + + /** + * The address of the contract writing this log message. + * + * @return The loggers address. + */ + Address getLogger(); + + /** + * The list of 32 byte log topics, possibly empty. + * + * @return The list, possibly zero length, of log topics. + */ + List getTopics(); + + /** + * The data, of possibly unlimited length, for this log entry. + * + * @return The log data. + */ + UnformattedData getData(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Quantity.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Quantity.java new file mode 100644 index 0000000000..9b7a865db6 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Quantity.java @@ -0,0 +1,35 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** + * An interface to mark the {@link BinaryData} that also represents a disceete quantity, such as an + * unsigned integer value. + */ +@Unstable +public interface Quantity extends BinaryData { + + /** + * Returns the numeric value of the quantity. + * + *

The specific class returned may be the boxed Java primitives, however plugin authors should + * not rely on the underlying number always being castable to that primitive in all cases and + * should instead rely on APIs such as {@link Number#longValue()} to cast to primitive values. + * Similarly the underlying object based values may evolve over time. + * + * @return The boxed or object based value of the quantity. + */ + Number getValue(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Transaction.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Transaction.java new file mode 100644 index 0000000000..b23c6e0f48 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/Transaction.java @@ -0,0 +1,130 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +import java.math.BigInteger; +import java.util.Optional; + +/** + * A transaction is a single cryptographically-signed instruction constructed by an actor externally + * to the scope of Ethereum. While it is assumed that the ultimate external actor will be human in + * nature, software tools will be used in its construction and dissemination. + * + *

There are two types of transactions: those which result in message calls and those which + * result in the creation of new accounts with associated code (known informally as ‘contract + * creation’). Message call transactions will have an address present in the {@link #getTo} method + * whereas contract creation transactions will not. + */ +@Unstable +public interface Transaction { + + /** + * A scalar value equal to the number of transactions sent by the sender. + * + * @return the number of transactions sent by the sender. + */ + long getNonce(); + + /** + * A scalar value equal to the number of Wei to be paid per unit of gas for all computation costs + * incurred as a result of the execution of this transaction. + * + * @return the quantity of Wei per gas unit paid. + */ + Quantity getGasPrice(); + + /** + * A scalar value equal to the maximum amount of gas that should be used in executing this + * transaction. This is paid up-front, before any computation is done and may not be increased + * later. + * + * @return the maximum amount of gas that should be used in executing this * transaction. + */ + long getGasLimit(); + + /** + * The 160-bit address of the message call’s recipient. For a contract creation transaction this + * address will not be present. + * + * @return address of the recipient + */ + Optional getTo(); + + /** + * A scalar value equal to the number of Wei to be transferred to the message call’s recipient or, + * in the case of contract creation, as an endowment to the newly created account + * + * @return value equal to the number of Wei to be transferred + */ + Quantity getValue(); + + /** + * Value corresponding to the 'V' component of the signature of the transaction. + * + * @return the 'V' component of the signature + */ + BigInteger getV(); + + /** + * Value corresponding to the 'V' component of the signature of the transaction. + * + * @return the 'V' component of the signature + */ + BigInteger getR(); + + /** + * Value corresponding to the 'V' component of the signature of the transaction. + * + * @return the 'V' component of the signature + */ + BigInteger getS(); + + /** + * The 160-bit address of the account sending the transaction, extracted from the v, r, s + * parameters. + * + * @return The address of the account that sent this transaction. + */ + Address getSender(); + + /** + * The chainId, computed from the 'V' portion of the signature. Used for replay protection. If + * replay protection is not enabled this value will not be present. + * + * @return The chainId for transaction. + */ + Optional getChainId(); + + /** + * An unlimited size byte array specifying the EVM-code for the account // initialisation + * procedure. + * + *

Only present if this is a contract creation transaction, which is only true if {@link + * #getTo} is empty. + * + * @return if present, the contract init code. + */ + Optional getInit(); + + /** + * An unlimited size byte array specifying theinput data of the message call. + * + *

Only present if this is a message call transaction, which is only true if {@link #getTo} is + * present. + * + * @return if present, the message call data + */ + Optional getData(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/UnformattedData.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/UnformattedData.java new file mode 100644 index 0000000000..d3515531be --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/data/UnformattedData.java @@ -0,0 +1,19 @@ +/* + * 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.plugin.data; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** An interface to mark BinaryData that is not a scalar {@link Quantity}. */ +@Unstable +public interface UnformattedData extends BinaryData {} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/MetricsSystem.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/MetricsSystem.java new file mode 100644 index 0000000000..bb1cef4843 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/MetricsSystem.java @@ -0,0 +1,119 @@ +/* + * Copyright 2018 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.plugin.services; + +import tech.pegasys.pantheon.plugin.services.metrics.Counter; +import tech.pegasys.pantheon.plugin.services.metrics.LabelledMetric; +import tech.pegasys.pantheon.plugin.services.metrics.MetricCategory; +import tech.pegasys.pantheon.plugin.services.metrics.OperationTimer; + +import java.util.function.DoubleSupplier; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; + +/** An interface for creating various Metrics components. */ +public interface MetricsSystem { + + /** + * Creates a Counter. + * + * @param category The {@link MetricCategory} this counter is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @return The created Counter instance. + */ + default Counter createCounter( + final MetricCategory category, final String name, final String help) { + return createLabelledCounter(category, name, help, new String[0]).labels(); + } + + /** + * Creates a Counter with assigned labels. + * + * @param category The {@link MetricCategory} this counter is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @param labelNames An array of labels to assign to the Counter. + * @return The created LabelledMetric instance. + */ + LabelledMetric createLabelledCounter( + MetricCategory category, String name, String help, String... labelNames); + + /** + * Creates a Timer. + * + * @param category The {@link MetricCategory} this timer is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @return The created Timer instance. + */ + default OperationTimer createTimer( + final MetricCategory category, final String name, final String help) { + return createLabelledTimer(category, name, help, new String[0]).labels(); + } + + /** + * Creates a Timer with assigned labels. + * + * @param category The {@link MetricCategory} this timer is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @param labelNames An array of labels to assign to the Timer. + * @return The created LabelledMetric instance. + */ + LabelledMetric createLabelledTimer( + MetricCategory category, String name, String help, String... labelNames); + + /** + * Creates a gauge for displaying double vales. A gauge is a metric to report the current value. + * The metric value may go up or down. + * + * @param category The {@link MetricCategory} this gauge is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @param valueSupplier A supplier for the double value to be presented. + */ + void createGauge(MetricCategory category, String name, String help, DoubleSupplier valueSupplier); + + /** + * Creates a gauge for displaying integer values. + * + * @param category The {@link MetricCategory} this gauge is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @param valueSupplier A supplier for the integer value to be presented. + */ + default void createIntegerGauge( + final MetricCategory category, + final String name, + final String help, + final IntSupplier valueSupplier) { + createGauge(category, name, help, () -> (double) valueSupplier.getAsInt()); + } + + /** + * Creates a gauge for displaying long values. + * + * @param category The {@link MetricCategory} this gauge is assigned to. + * @param name A name for this metric. + * @param help A human readable description of the metric. + * @param valueSupplier A supplier for the long value to be presented. + */ + default void createLongGauge( + final MetricCategory category, + final String name, + final String help, + final LongSupplier valueSupplier) { + createGauge(category, name, help, () -> (double) valueSupplier.getAsLong()); + } +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonConfiguration.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonConfiguration.java new file mode 100644 index 0000000000..6a0473a4f6 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonConfiguration.java @@ -0,0 +1,36 @@ +/* + * 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.plugin.services; + +import java.net.URI; +import java.nio.file.Path; +import java.util.Optional; + +/** Generally useful configuration provided by Pantheon. */ +public interface PantheonConfiguration { + + /** + * Location of the working directory of the storage in the file system running the client. + * + * @return location of the storage in the file system of the client. + */ + Path getStoragePath(); + + /** + * Url of the enclave that stores private transaction data. + * + * @return an optional containing the url of the enclave Pantheon is connected to, or empty if + * privacy is not enabled. + */ + Optional getEnclaveUrl(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonEvents.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonEvents.java new file mode 100644 index 0000000000..18a99fa343 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PantheonEvents.java @@ -0,0 +1,117 @@ +/* + * 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.plugin.services; + +import tech.pegasys.pantheon.plugin.Unstable; +import tech.pegasys.pantheon.plugin.data.BlockHeader; +import tech.pegasys.pantheon.plugin.data.Transaction; + +/** + * This service allows plugins to attach to various events during the normal operation of Pantheon. + * + *

Currently supported events + * + *

    + *
  • newBlockPropagated - Fired when a new block header has been received and validated + * and is about to be sent out to other peers, but before the body of the block has been + * evaluated and validated. + *
  • newTransactionAdded - Fired when a new transaction has been added to the node. + *
+ */ +@Unstable +public interface PantheonEvents { + + /** + * Add a listener watching new blocks propagated. + * + * @param newBlockPropagatedListener The listener that will accept a BlockHeader as the event. + * @return an object to be used as an identifier when de-registering the event. + */ + Object addNewBlockPropagatedListener(NewBlockPropagatedListener newBlockPropagatedListener); + + /** + * Remove the blockAdded listener from pantheon notifications. + * + * @param listenerIdentifier The instance that was returned from addBlockAddedListener; + */ + void removeNewBlockPropagatedListener(Object listenerIdentifier); + + /** + * Add a listener watching new transactions added to the node. + * + * @param newTransactionAddedListener The listener that will accept the Transaction object as the + * event. + * @return an object to be used as an identifier when de-registering the event. + */ + Object addNewTransactionAddedListener(NewTransactionAddedListener newTransactionAddedListener); + + /** + * Remove the blockAdded listener from pantheon notifications. + * + * @param listenerIdentifier The instance that was returned from addNewTransactionAddedListener; + */ + void removeNewTransactionAddedListener(Object listenerIdentifier); + + /** + * Add a listener watching dropped transactions. + * + * @param newTransactionDroppedListener The listener that will accept the Transaction object as + * the event. + * @return an object to be used as an identifier when de-registering the event. + */ + Object addNewTransactionDroppedListener(TransactionDroppedListener newTransactionDroppedListener); + + /** + * Remove the transactionDropped listener from pantheon notifications. + * + * @param listenerIdentifier The instance that was returned from addTransactionDroppedListener; + */ + void removeTransactionDroppedListener(Object listenerIdentifier); + + /** The listener interface for receiving new block propagated events. */ + interface NewBlockPropagatedListener { + + /** + * Invoked when a new block header has been received and validated and is about to be sent out + * to other peers, but before the body of the block has been evaluated and validated. + * + *

The block may not have been imported to the local chain yet and may fail later + * validations. + * + * @param newBlockHeader the new block header. + */ + void newBlockPropagated(BlockHeader newBlockHeader); + } + + /** The listener interface for receiving new transaction added events. */ + interface NewTransactionAddedListener { + + /** + * Invoked when a new transaction has been added to the node. + * + * @param transaction the new transaction. + */ + void newTransactionAdded(Transaction transaction); + } + + /** The listener interface for receiving transaction dropped events. */ + interface TransactionDroppedListener { + + /** + * Invoked when a transaction is dropped from the node. + * + * @param transaction the dropped transaction. + */ + void newTransactionDropped(Transaction transaction); + } +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PicoCLIOptions.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PicoCLIOptions.java new file mode 100644 index 0000000000..f6814ddc5b --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/PicoCLIOptions.java @@ -0,0 +1,37 @@ +/* + * 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.plugin.services; + +/** + * A service that plugins can use to add CLI options and commands to the PantheonCommand. The + * PicoCLI library annotations will be inspected and the object will be passed into a + * picocli.CommandLine.addMixin call. + * + *

This service will be available during the registration callbacks. + * + *

CLI arguments should conform to the CLI-STYLE-GUIDE.md + * conventions. + */ +public interface PicoCLIOptions { + + /** + * During the registration callback plugins can register CLI options that should be added to + * Pantheon's CLI startup. + * + * @param namespace A namespace prefix. All registered options must start with this prefix + * @param optionObject The instance of the object to be inspected. PicoCLI will reflect the fields + * of this object to extract the CLI options. + */ + void addPicoCLIOptions(String namespace, Object optionObject); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/StorageService.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/StorageService.java new file mode 100644 index 0000000000..e5fbdf0367 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/StorageService.java @@ -0,0 +1,38 @@ +/* + * 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.plugin.services; + +import tech.pegasys.pantheon.plugin.Unstable; +import tech.pegasys.pantheon.plugin.services.storage.KeyValueStorageFactory; +import tech.pegasys.pantheon.plugin.services.storage.SegmentIdentifier; + +import java.util.List; + +/** This service allows plugins to register as an available storage engine. */ +@Unstable +public interface StorageService { + + /** + * Registers a factory as available for creating key-value storage instances. + * + * @param factory creates instances providing key-value storage. + */ + void registerKeyValueStorage(KeyValueStorageFactory factory); + + /** + * Retrieves the identifiers for the isolation segments that could be requested during operation. + * + * @return full set of possible segments required from the storage service. + */ + List getAllSegmentIdentifiers(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/exception/StorageException.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/exception/StorageException.java new file mode 100644 index 0000000000..745f6528b9 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/exception/StorageException.java @@ -0,0 +1,47 @@ +/* + * 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.plugin.services.exception; + +/** Base exception class for problems encountered in the domain for storage. */ +public class StorageException extends RuntimeException { + + /** + * Constructs a new storage exception with the specified cause. + * + * @param cause saved for later retrieval by the {@link #getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + */ + public StorageException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new storage exception with the specified detail message and cause. + * + * @param message the detail that may be retrieved later by Throwable.getMessage(). + * @param cause saved for later retrieval by the {@link #getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + */ + public StorageException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new storage exception with the specified detail message. + * + * @param message the detail that may be retrieved later by Throwable.getMessage(). + */ + public StorageException(final String message) { + super(message); + } +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/Counter.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/Counter.java new file mode 100644 index 0000000000..59be762e8f --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/Counter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 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.plugin.services.metrics; + +/** + * A counter is a metric to track counts of events or running totals etc. The value of the counter + * can only increase. + */ +public interface Counter { + + /** Increment the counter by 1. */ + void inc(); + + /** + * Increment the counter by a specified amount. + * + * @param amount The amount to increment the counter by. Must be greater than or equal to 0. + */ + void inc(long amount); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/LabelledMetric.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/LabelledMetric.java new file mode 100644 index 0000000000..239aa7510c --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/LabelledMetric.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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.plugin.services.metrics; + +/** + * A metric with labels associated. Values for the associated labels can be provided to access the + * underlying metric. + * + * @param The type of metric the labels are applied to. + */ +public interface LabelledMetric { + + /** + * Returns a metric tagged with the specified label values. + * + * @param labels An array of label values in the same order as the labels when creating this + * metric. The number of values provided must match the number of labels. + * @return A metric tagged with the specified labels. + */ + T labels(String... labels); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategory.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategory.java new file mode 100644 index 0000000000..d7ed37c74a --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategory.java @@ -0,0 +1,43 @@ +/* + * 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.plugin.services.metrics; + +import java.util.Optional; + +/** + * A MetricCategory is used to group related metrics. Every metric belongs to one and only one + * MetricCategory. + * + *

Categories must be registered with the {@link MetricCategoryRegistry} during plugin + * initialisation. + */ +public interface MetricCategory { + + /** + * Gets the name of this MetricCategory. + * + * @return The name of this MetricCategory. + */ + String getName(); + + /** + * Gets the application-specific MetricCategory prefix. An empty Optional may be returned if this + * category is not application specific. + * + *

The prefix, if present, is prepended to the category name when creating a single combined + * name for metrics. + * + * @return An optional application prefix. + */ + Optional getApplicationPrefix(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategoryRegistry.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategoryRegistry.java new file mode 100644 index 0000000000..bc4e3e2e84 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/MetricCategoryRegistry.java @@ -0,0 +1,29 @@ +/* + * 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.plugin.services.metrics; + +/** + * Allow registration of {@link MetricCategory} instances so they are recognised by the metrics + * system and can be enabled. + * + *

Categories must be registered during plugin initialisation. + */ +public interface MetricCategoryRegistry { + + /** + * Registers a {@link MetricCategory}. + * + * @param newMetricCategory The {@link MetricCategory} that is being registered. + */ + public void addMetricCategory(final MetricCategory newMetricCategory); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/OperationTimer.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/OperationTimer.java new file mode 100644 index 0000000000..1a9eaee5b8 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/metrics/OperationTimer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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.plugin.services.metrics; + +import java.io.Closeable; + +/** A timer metric that records duration of operations for metrics purposes. */ +public interface OperationTimer { + + /** + * Starts the timer. + * + * @return The produced TimingContext, which must be stopped or closed when the operation being + * timed has completed. + */ + TimingContext startTimer(); + + /** An interface for stopping the timer and returning elapsed time. */ + interface TimingContext extends Closeable { + + /** + * Stops the timer and returns the elapsed time. + * + * @return Elapsed time in seconds. + */ + double stopTimer(); + + @Override + default void close() { + stopTimer(); + } + } +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorage.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorage.java new file mode 100644 index 0000000000..44208522fc --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorage.java @@ -0,0 +1,78 @@ +/* + * 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.plugin.services.storage; + +import tech.pegasys.pantheon.plugin.Unstable; +import tech.pegasys.pantheon.plugin.services.exception.StorageException; + +import java.io.Closeable; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * Responsible for storing values against keys. + * + *

Behaviour expected with regard to key to value mapping is that of a map, one key maps to one + * value, when a new value is added with an existing key, that key now points at the new value. + * + *

All keys and values must be non-null. + */ +@Unstable +public interface KeyValueStorage extends Closeable { + + /** + * Deletes all keys and values from the storage. + * + * @throws StorageException problem encountered when attempting to clear storage. + */ + void clear() throws StorageException; + + /** + * Whether the key-value storage contains the given key. + * + * @param key a key that might be contained in the key-value storage. + * @return true when the given key is present in keyset, false + * otherwise. + * @throws StorageException problem encountered when interacting with the key set. + */ + boolean containsKey(byte[] key) throws StorageException; + + /** + * Retrieves the value associated with a given key. + * + * @param key whose associated value is being retrieved. + * @return an {@link Optional} containing the value associated with the specified key, otherwise + * empty. + * @throws StorageException problem encountered during the retrieval attempt. + */ + Optional get(byte[] key) throws StorageException; + + /** + * Performs an evaluation against each key in the store, keeping the entries that pass, removing + * those that fail. + * + * @param retainCondition predicate to evaluate each key against, unless the result is {@code + * null}, both the key and associated value must be removed. + * @return the number of keys removed. + * @throws StorageException problem encountered when removing data. + */ + long removeAllKeysUnless(Predicate retainCondition) throws StorageException; + + /** + * Begins a fresh transaction, for sequencing operations for later atomic execution. + * + * @return transaciton to sequence key-value operations. + * @throws StorageException problem encountered when starting a new transaction. + */ + KeyValueStorageTransaction startTransaction() throws StorageException; +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageFactory.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageFactory.java new file mode 100644 index 0000000000..8691cf06b1 --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageFactory.java @@ -0,0 +1,57 @@ +/* + * 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.plugin.services.storage; + +import tech.pegasys.pantheon.plugin.Unstable; +import tech.pegasys.pantheon.plugin.services.exception.StorageException; + +/** Factory for creating key-value storage instances. */ +@Unstable +public interface KeyValueStorageFactory { + + /** + * Retrieves the identity of the key-value storage factory. + * + * @return the storage identifier, used when selecting the appropriate storage service. + */ + String getName(); + + /** + * Creates a new key-value storage instance, appropriate for the given segment. + * + *

When segment isolation is not supported, the create will still be called with each of the + * required segments, where the same storage instance should be returned. + * + *

New segments may be introduced in future releases and should result in a new empty + * key-space. Segments created with the identifier of an existing segment should have the same + * data as that existing segment. + * + * @param segment identity of the isolation segment, an identifier for the data set the storage + * will contain. + * @return the storage instance reserved for the given segment. + * @exception StorageException problem encountered when creating storage for the segment. + */ + KeyValueStorage create(SegmentIdentifier segment) throws StorageException; + + /** + * Whether storage segment isolation is supported by the factory created instances. + * + *

As supporting segment isolation is similar to a separating keys into distinct namespaces, + * where operations only affect within that segment i.e. the same key from two segments point to + * separate values. + * + * @return true when the created storage instances are isolated from each other, + * false when keys of different segments can collide with each other. + */ + boolean isSegmentIsolationSupported(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageTransaction.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageTransaction.java new file mode 100644 index 0000000000..bda917e64c --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/KeyValueStorageTransaction.java @@ -0,0 +1,49 @@ +/* + * 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.plugin.services.storage; + +import tech.pegasys.pantheon.plugin.Unstable; +import tech.pegasys.pantheon.plugin.services.exception.StorageException; + +/** A transaction that can atomically commit a sequence of operations to a key-value store. */ +@Unstable +public interface KeyValueStorageTransaction { + + /** + * Associates the specified value with the specified key. + * + *

If a previously value had been store against the given key, the old value is replaced by the + * given value. + * + * @param key the given value is to be associated with. + * @param value associated with the specified key. + */ + void put(byte[] key, byte[] value); + + /** + * When the given key is present, the key and mapped value will be removed from storage. + * + * @param key the key and mapped value that will be removed. + */ + void remove(byte[] key); + + /** + * Performs an atomic commit of all the operations queued in the transaction. + * + * @throws StorageException problem was encountered preventing the commit + */ + void commit() throws StorageException; + + /** Reset the transaction to a state prior to any operations being queued. */ + void rollback(); +} diff --git a/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/SegmentIdentifier.java b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/SegmentIdentifier.java new file mode 100644 index 0000000000..659232cd6e --- /dev/null +++ b/plugins/src/main/java/tech/pegasys/pantheon/plugin/services/storage/SegmentIdentifier.java @@ -0,0 +1,30 @@ +/* + * 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.plugin.services.storage; + +import tech.pegasys.pantheon.plugin.Unstable; + +/** + * A namespace identifier for the storage instance segment, a grouping of data that should be kept + * isolated from the data of other segments. + */ +@Unstable +public interface SegmentIdentifier { + + /** + * Identifier for the segment consistent throughout the lifetime of the segment. + * + * @return unique name of the segment. + */ + String getName(); +} diff --git a/services/kvstore/build.gradle b/services/kvstore/build.gradle index e1d57049ea..030a467a43 100644 --- a/services/kvstore/build.gradle +++ b/services/kvstore/build.gradle @@ -26,10 +26,9 @@ jar { } dependencies { + api project(':plugins') api project(':util') - api 'tech.pegasys.pantheon:plugin-api' - implementation project(':metrics:core') implementation project(':metrics:rocksdb') implementation project(':services:util') diff --git a/services/pipeline/build.gradle b/services/pipeline/build.gradle index d38f1f1247..ffa7bb60e4 100644 --- a/services/pipeline/build.gradle +++ b/services/pipeline/build.gradle @@ -28,10 +28,10 @@ jar { dependencies { api project(':util') implementation project(':metrics:core') + implementation project(':plugins') implementation 'org.apache.logging.log4j:log4j-api' implementation 'com.google.guava:guava' - implementation 'tech.pegasys.pantheon:plugin-api' runtime 'org.apache.logging.log4j:log4j-core' diff --git a/services/tasks/build.gradle b/services/tasks/build.gradle index 5990f20265..7fa1067f0c 100644 --- a/services/tasks/build.gradle +++ b/services/tasks/build.gradle @@ -26,10 +26,9 @@ jar { } dependencies { + api project(':plugins') api project(':util') - api 'tech.pegasys.pantheon:plugin-api' - compileOnly 'org.openjdk.jmh:jmh-generator-annprocess' implementation project(':metrics:core') diff --git a/settings.gradle b/settings.gradle index a3c182fb43..429facdfd7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -37,6 +37,7 @@ include 'metrics:core' include 'metrics:rocksdb' include 'nat' include 'pantheon' +include 'plugins' include 'services:kvstore' include 'services:pipeline' include 'services:tasks' diff --git a/util/build.gradle b/util/build.gradle index dba58668f8..f1dd867cdc 100644 --- a/util/build.gradle +++ b/util/build.gradle @@ -26,10 +26,10 @@ jar { } dependencies { + implementation project(':plugins') implementation 'com.google.guava:guava' implementation 'io.vertx:vertx-core' implementation 'org.apache.logging.log4j:log4j-api' - implementation 'tech.pegasys.pantheon:plugin-api' runtime 'org.apache.logging.log4j:log4j-core'