-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from strategyobject/feature/pallet-query-api
Annotations for pallets and their storages
- Loading branch information
Showing
73 changed files
with
1,505 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,18 @@ | ||
dependencies { | ||
implementation project(':rpc') | ||
implementation project(':rpc:rpc-core') | ||
implementation project(':rpc:rpc-sections') | ||
implementation project(':transport') | ||
implementation project(':pallet') | ||
implementation project(':scale') | ||
implementation project(':types') | ||
implementation project(':rpc:rpc-types') | ||
implementation project(':storage') | ||
|
||
testImplementation project(':tests') | ||
|
||
testImplementation 'org.testcontainers:testcontainers:1.16.3' | ||
testImplementation 'org.testcontainers:junit-jupiter:1.16.3' | ||
|
||
testAnnotationProcessor project(':pallet:pallet-codegen') | ||
} |
26 changes: 26 additions & 0 deletions
26
api/src/main/java/com/strategyobject/substrateclient/api/Api.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,33 @@ | ||
package com.strategyobject.substrateclient.api; | ||
|
||
import com.strategyobject.substrateclient.pallet.GeneratedPalletResolver; | ||
import com.strategyobject.substrateclient.rpc.Rpc; | ||
import com.strategyobject.substrateclient.rpc.RpcImpl; | ||
import com.strategyobject.substrateclient.transport.ProviderInterface; | ||
import lombok.val; | ||
|
||
/** | ||
* Provides the ability to query a node and interact with the Polkadot or Substrate chains. | ||
* It allows interacting with blockchain in various ways: using RPC's queries directly or | ||
* accessing Pallets and its APIs, such as storages, transactions, etc. | ||
*/ | ||
public interface Api { | ||
static DefaultApi with(ProviderInterface provider) { | ||
val rpc = RpcImpl.with(provider); | ||
|
||
return DefaultApi.with(rpc, GeneratedPalletResolver.with(rpc)); | ||
} | ||
|
||
/** | ||
* @return the instance that provides a proper API for querying the RPC's methods. | ||
*/ | ||
Rpc rpc(); | ||
|
||
/** | ||
* Resolves the instance of a pallet by its definition. | ||
* @param clazz the class of the pallet | ||
* @param <T> the type of the pallet | ||
* @return appropriate instance of the pallet | ||
*/ | ||
<T> T pallet(Class<T> clazz); | ||
} |
34 changes: 34 additions & 0 deletions
34
api/src/main/java/com/strategyobject/substrateclient/api/DefaultApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.strategyobject.substrateclient.api; | ||
|
||
import com.strategyobject.substrateclient.pallet.PalletResolver; | ||
import com.strategyobject.substrateclient.rpc.Rpc; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
@RequiredArgsConstructor(staticName = "with") | ||
public class DefaultApi implements Api, AutoCloseable { | ||
private final @NonNull Rpc rpc; | ||
private final @NonNull PalletResolver palletResolver; | ||
private final Map<Class<?>, Object> palletCache = new ConcurrentHashMap<>(); | ||
|
||
@Override | ||
public Rpc rpc() { | ||
return rpc; | ||
} | ||
|
||
@Override | ||
public <T> T pallet(@NonNull Class<T> clazz) { | ||
return clazz.cast(palletCache | ||
.computeIfAbsent(clazz, palletResolver::resolve)); | ||
} | ||
|
||
@Override | ||
public void close() throws Exception { | ||
if (rpc instanceof AutoCloseable) { | ||
((AutoCloseable) rpc).close(); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
api/src/test/java/com/strategyobject/substrateclient/api/ApiTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.strategyobject.substrateclient.api; | ||
|
||
import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; | ||
import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; | ||
import com.strategyobject.substrateclient.transport.ws.WsProvider; | ||
import lombok.val; | ||
import org.junit.jupiter.api.Test; | ||
import org.testcontainers.junit.jupiter.Container; | ||
import org.testcontainers.junit.jupiter.Testcontainers; | ||
|
||
import java.math.BigInteger; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
@Testcontainers | ||
public class ApiTests { | ||
private static final int WAIT_TIMEOUT = 1000; | ||
|
||
@Container | ||
private final TestSubstrateContainer substrate = new TestSubstrateContainer(SubstrateVersion.V3_0_0); | ||
|
||
@Test | ||
public void getSystemPalletAndCall() throws Exception { // TODO move the test out of the project | ||
val wsProvider = WsProvider.builder() | ||
.setEndpoint(substrate.getWsAddress()) | ||
.build(); | ||
wsProvider.connect().get(WAIT_TIMEOUT, TimeUnit.SECONDS); | ||
|
||
try (val api = Api.with(wsProvider)) { | ||
val systemPallet = api.pallet(SystemPallet.class); | ||
val blockHash = systemPallet | ||
.blockHash() | ||
.get(0) | ||
.get(WAIT_TIMEOUT, TimeUnit.SECONDS); | ||
|
||
assertNotNull(blockHash); | ||
assertNotEquals(BigInteger.ZERO, new BigInteger(blockHash.getData())); | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
api/src/test/java/com/strategyobject/substrateclient/api/SystemPallet.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.strategyobject.substrateclient.api; | ||
|
||
import com.strategyobject.substrateclient.pallet.annotations.Pallet; | ||
import com.strategyobject.substrateclient.pallet.annotations.Storage; | ||
import com.strategyobject.substrateclient.pallet.annotations.StorageHasher; | ||
import com.strategyobject.substrateclient.pallet.annotations.StorageKey; | ||
import com.strategyobject.substrateclient.rpc.types.BlockHash; | ||
import com.strategyobject.substrateclient.scale.annotations.Scale; | ||
import com.strategyobject.substrateclient.storage.StorageNMap; | ||
|
||
@Pallet("System") | ||
public interface SystemPallet { | ||
@Storage( | ||
value = "BlockHash", | ||
keys = { | ||
@StorageKey( | ||
type = @Scale(Integer.class), | ||
hasher = StorageHasher.TwoX64Concat | ||
) | ||
}) | ||
StorageNMap<BlockHash> blockHash(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
dependencies { | ||
implementation project(':scale') | ||
implementation project(':rpc') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
dependencies { | ||
implementation project(':common') | ||
implementation project(':rpc') | ||
implementation project(':types') | ||
implementation project(':scale') | ||
implementation project(':scale:scale-codegen') | ||
implementation project(':storage') | ||
implementation project(':pallet') | ||
|
||
implementation 'com.squareup:javapoet:1.13.0' | ||
|
||
compileOnly 'com.google.auto.service:auto-service-annotations:1.0.1' | ||
annotationProcessor 'com.google.auto.service:auto-service:1.0.1' | ||
|
||
testImplementation 'com.google.testing.compile:compile-testing:0.19' | ||
} |
32 changes: 32 additions & 0 deletions
32
...egen/src/main/java/com.strategyobject.substrateclient.pallet/CompoundMethodProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.strategyobject.substrateclient.pallet; | ||
|
||
import com.squareup.javapoet.MethodSpec; | ||
import com.squareup.javapoet.TypeSpec; | ||
import com.strategyobject.substrateclient.common.codegen.ProcessingException; | ||
import com.strategyobject.substrateclient.common.codegen.ProcessorContext; | ||
import lombok.NonNull; | ||
import lombok.var; | ||
|
||
import javax.lang.model.element.ExecutableElement; | ||
import javax.lang.model.element.TypeElement; | ||
import java.util.List; | ||
|
||
class CompoundMethodProcessor extends PalletMethodProcessor { | ||
private final List<PalletMethodProcessor> processors; | ||
|
||
public CompoundMethodProcessor(TypeElement typeElement, List<PalletMethodProcessor> processors) { | ||
super(typeElement); | ||
this.processors = processors; | ||
} | ||
|
||
@Override | ||
void process(@NonNull String palletName, @NonNull ExecutableElement method, TypeSpec.@NonNull Builder typeSpecBuilder, MethodSpec.Builder constructorBuilder, @NonNull ProcessorContext context) throws ProcessingException { | ||
for (var processor : processors) { | ||
processor.process(palletName, | ||
method, | ||
typeSpecBuilder, | ||
constructorBuilder, | ||
context); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
pallet/pallet-codegen/src/main/java/com.strategyobject.substrateclient.pallet/Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.strategyobject.substrateclient.pallet; | ||
|
||
class Constants { | ||
static final String RPC = "rpc"; | ||
static final String CLASS_NAME_TEMPLATE = "%sImpl"; | ||
static final String SCALE_READER_REGISTRY = "scaleReaderRegistry"; | ||
static final String SCALE_WRITER_REGISTRY = "scaleWriterRegistry"; | ||
static final String STORAGE_FACTORY_METHOD = "with"; | ||
static final String STORAGE_KEY_PROVIDER_FACTORY_METHOD = "of"; | ||
static final String STORAGE_KEY_PROVIDER_ADD_HASHERS = "use"; | ||
static final String KEY_HASHER_FACTORY_METHOD = "with"; | ||
static final String BLAKE_2_128_CONCAT_INSTANCE = "getInstance"; | ||
static final String TWO_X64_CONCAT_INSTANCE = "getInstance"; | ||
static final String IDENTITY_INSTANCE = "getInstance"; | ||
} |
83 changes: 83 additions & 0 deletions
83
...gen/src/main/java/com.strategyobject.substrateclient.pallet/PalletAnnotatedInterface.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package com.strategyobject.substrateclient.pallet; | ||
|
||
import com.google.common.base.Strings; | ||
import com.squareup.javapoet.JavaFile; | ||
import com.squareup.javapoet.MethodSpec; | ||
import com.squareup.javapoet.TypeName; | ||
import com.squareup.javapoet.TypeSpec; | ||
import com.strategyobject.substrateclient.common.codegen.ProcessingException; | ||
import com.strategyobject.substrateclient.common.codegen.ProcessorContext; | ||
import com.strategyobject.substrateclient.pallet.annotations.Pallet; | ||
import com.strategyobject.substrateclient.rpc.Rpc; | ||
import lombok.val; | ||
|
||
import javax.lang.model.element.ExecutableElement; | ||
import javax.lang.model.element.Modifier; | ||
import javax.lang.model.element.TypeElement; | ||
import java.io.IOException; | ||
|
||
import static com.strategyobject.substrateclient.pallet.Constants.CLASS_NAME_TEMPLATE; | ||
import static com.strategyobject.substrateclient.pallet.Constants.RPC; | ||
|
||
public class PalletAnnotatedInterface { | ||
private final TypeElement interfaceElement; | ||
private final String name; | ||
private final PalletMethodProcessor methodProcessor; | ||
|
||
public PalletAnnotatedInterface(TypeElement interfaceElement, PalletMethodProcessor methodProcessor) throws ProcessingException { | ||
this.interfaceElement = interfaceElement; | ||
val annotation = interfaceElement.getAnnotation(Pallet.class); | ||
|
||
if (!interfaceElement.getModifiers().contains(Modifier.PUBLIC)) { | ||
throw new ProcessingException( | ||
interfaceElement, | ||
"`%s` is not public. That is not allowed.", | ||
interfaceElement.getQualifiedName().toString()); | ||
} | ||
|
||
if (Strings.isNullOrEmpty(name = annotation.value())) { | ||
throw new ProcessingException( | ||
interfaceElement, | ||
"`@%s` of `%s` contains null or empty `value`.", | ||
annotation.getClass().getSimpleName(), | ||
interfaceElement.getQualifiedName().toString()); | ||
} | ||
|
||
this.methodProcessor = methodProcessor; | ||
} | ||
|
||
public void generateClass(ProcessorContext context) throws ProcessingException, IOException { | ||
val interfaceName = interfaceElement.getSimpleName().toString(); | ||
val className = String.format(CLASS_NAME_TEMPLATE, interfaceName); | ||
val packageName = context.getPackageName(interfaceElement); | ||
|
||
val typeSpecBuilder = TypeSpec.classBuilder(className) | ||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL) | ||
.addSuperinterface(TypeName.get(interfaceElement.asType())) | ||
.addField(Rpc.class, RPC, Modifier.FINAL, Modifier.PRIVATE); | ||
|
||
val constructorBuilder = createConstructorBuilder(); | ||
|
||
for (val method : interfaceElement.getEnclosedElements()) { | ||
this.methodProcessor.process(name, | ||
(ExecutableElement) method, | ||
typeSpecBuilder, | ||
constructorBuilder, | ||
context); | ||
} | ||
|
||
typeSpecBuilder.addMethod(constructorBuilder.build()); | ||
|
||
JavaFile.builder(packageName, typeSpecBuilder.build()).build().writeTo(context.getFiler()); | ||
} | ||
|
||
private MethodSpec.Builder createConstructorBuilder() { | ||
return MethodSpec.constructorBuilder() | ||
.addModifiers(Modifier.PUBLIC) | ||
.addParameter(Rpc.class, RPC) | ||
.beginControlFlow("if ($L == null)", RPC) | ||
.addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, RPC) | ||
.endControlFlow() | ||
.addStatement("this.$1L = $1L", RPC); | ||
} | ||
} |
Oops, something went wrong.