diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/Constants.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/Constants.java index 2936bc62..1c9596b7 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/Constants.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/Constants.java @@ -7,6 +7,7 @@ public class Constants { public static final String AUTO_REGISTER_TYPES_ARG = "types"; public static final String PAIR_FACTORY_METHOD = "of"; + public static final String RESOLVE_AND_INJECT_METHOD = "resolveAndInjectOrNull"; public static final String ENCODE_METHOD_NAME = "encode"; public static final String DECODE_METHOD_NAME = "decode"; diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/DecoderCompositor.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/DecoderCompositor.java index c5e93a7b..f373c0ce 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/DecoderCompositor.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/DecoderCompositor.java @@ -4,7 +4,9 @@ import com.strategyobject.substrateclient.common.codegen.TypeTraverser; import com.strategyobject.substrateclient.rpc.core.DecoderPair; import com.strategyobject.substrateclient.rpc.core.RpcDecoder; +import com.strategyobject.substrateclient.rpc.core.RpcRegistryHelper; import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.ScaleRegistryHelper; import lombok.NonNull; import lombok.var; @@ -16,12 +18,14 @@ import java.util.Map; import static com.strategyobject.substrateclient.rpc.codegen.Constants.PAIR_FACTORY_METHOD; +import static com.strategyobject.substrateclient.rpc.codegen.Constants.RESOLVE_AND_INJECT_METHOD; public class DecoderCompositor extends TypeTraverser { private final Types typeUtils; private final Map typeVarMap; private final String decoderAccessor; private final String readerAccessor; + private final String readerMethod; private final String decoderRegistryVarName; private final String scaleRegistryVarName; @@ -29,6 +33,7 @@ public DecoderCompositor(@NonNull Types typeUtils, @NonNull Map typeVarMap, @NonNull String decoderAccessor, @NonNull String readerAccessor, + @NonNull String readerMethod, @NonNull String decoderRegistryVarName, @NonNull String scaleRegistryVarName) { super(CodeBlock.class); @@ -36,6 +41,7 @@ public DecoderCompositor(@NonNull Types typeUtils, this.typeVarMap = typeVarMap; this.decoderAccessor = decoderAccessor; this.readerAccessor = readerAccessor; + this.readerMethod = readerMethod; this.decoderRegistryVarName = decoderRegistryVarName; this.scaleRegistryVarName = scaleRegistryVarName; } @@ -77,16 +83,17 @@ protected CodeBlock whenGenericType(@NonNull DeclaredType type, TypeMirror _over var builder = CodeBlock.builder() .add("$T.$L(", DecoderPair.class, PAIR_FACTORY_METHOD) - .add("($T) $L.resolve($T.class).inject(", RpcDecoder.class, decoderRegistryVarName, resolveType); + .add("$T.$L($T.class, ", RpcRegistryHelper.class, RESOLVE_AND_INJECT_METHOD, resolveType); for (var i = 0; i < subtypes.length; i++) { if (i > 0) builder.add(", "); builder.add(subtypes[i]); } - builder.add("), ($T) $L.resolve($T.class).inject(", ScaleReader.class, scaleRegistryVarName, resolveType); + builder.add("), $T.$L($T.class, ", ScaleRegistryHelper.class, RESOLVE_AND_INJECT_METHOD, resolveType); for (var i = 0; i < subtypes.length; i++) { if (i > 0) builder.add(", "); builder.add(subtypes[i]); + builder.add(".$L", readerMethod); } builder.add(")"); diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderAnnotatedClass.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderAnnotatedClass.java index 94054e51..6574764c 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderAnnotatedClass.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderAnnotatedClass.java @@ -121,6 +121,7 @@ private void setFields(MethodSpec.Builder methodSpec, ProcessorContext context) typeVarMap, String.format("%s[$L].%s", DECODERS_ARG, DECODER_UNSAFE_ACCESSOR), String.format("%s[$L].%s", DECODERS_ARG, READER_UNSAFE_ACCESSOR), + READER_UNSAFE_ACCESSOR, DECODER_REGISTRY, SCALE_READER_REGISTRY); val scaleAnnotationParser = new ScaleAnnotationParser(context); diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/EncoderCompositor.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/EncoderCompositor.java index a06bab2d..735715d0 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/EncoderCompositor.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/EncoderCompositor.java @@ -5,6 +5,8 @@ import com.strategyobject.substrateclient.common.codegen.TypeTraverser; import com.strategyobject.substrateclient.rpc.core.EncoderPair; import com.strategyobject.substrateclient.rpc.core.RpcEncoder; +import com.strategyobject.substrateclient.rpc.core.RpcRegistryHelper; +import com.strategyobject.substrateclient.scale.ScaleRegistryHelper; import com.strategyobject.substrateclient.scale.ScaleWriter; import lombok.NonNull; import lombok.val; @@ -16,8 +18,7 @@ import javax.lang.model.type.TypeVariable; import java.util.Map; -import static com.strategyobject.substrateclient.rpc.codegen.Constants.PAIR_FACTORY_METHOD; -import static com.strategyobject.substrateclient.rpc.codegen.Constants.RPC_SELF_ENCODABLE; +import static com.strategyobject.substrateclient.rpc.codegen.Constants.*; public class EncoderCompositor extends TypeTraverser { private final ProcessorContext context; @@ -25,6 +26,7 @@ public class EncoderCompositor extends TypeTraverser { private final TypeMirror selfEncodable; private final String encoderAccessor; private final String writerAccessor; + private final String writerMethod; private final String encoderRegistryVarName; private final String scaleRegistryVarName; @@ -32,6 +34,7 @@ public EncoderCompositor(@NonNull ProcessorContext context, @NonNull Map typeVarMap, @NonNull String encoderAccessor, @NonNull String writerAccessor, + @NonNull String writerMethod, @NonNull String encoderRegistryVarName, @NonNull String scaleRegistryVarName) { super(CodeBlock.class); @@ -40,6 +43,7 @@ public EncoderCompositor(@NonNull ProcessorContext context, this.selfEncodable = context.erasure(context.getType(RPC_SELF_ENCODABLE)); this.encoderAccessor = encoderAccessor; this.writerAccessor = writerAccessor; + this.writerMethod = writerMethod; this.encoderRegistryVarName = encoderRegistryVarName; this.scaleRegistryVarName = scaleRegistryVarName; } @@ -89,23 +93,23 @@ private CodeBlock getNonGenericCodeBlock(TypeMirror type) { protected CodeBlock whenGenericType(@NonNull DeclaredType type, TypeMirror _override, @NonNull CodeBlock[] subtypes) { TypeMirror resolveType = context.erasure(type); val builder = CodeBlock.builder() - .add("$T.$L(($T) ", EncoderPair.class, PAIR_FACTORY_METHOD, RpcEncoder.class); + .add("$T.$L(", EncoderPair.class, PAIR_FACTORY_METHOD); if (context.isSubtypeOf(resolveType, selfEncodable)) { - builder.add("registry.resolve($T.class)", selfEncodable); + builder.add("($T) registry.resolve($T.class)", selfEncodable, RpcEncoder.class); } else { - builder.add("$L.resolve($T.class).inject(", encoderRegistryVarName, resolveType); + builder.add("$T.$L($T.class, ", RpcRegistryHelper.class, RESOLVE_AND_INJECT_METHOD, resolveType); for (var i = 0; i < subtypes.length; i++) { if (i > 0) builder.add(", "); builder.add(subtypes[i]); } - builder.add(")"); } - builder.add("), ($T) $L.resolve($T.class).inject(", ScaleWriter.class, scaleRegistryVarName, resolveType); + builder.add("), $T.$L($T.class, ", ScaleRegistryHelper.class, RESOLVE_AND_INJECT_METHOD, resolveType); for (var i = 0; i < subtypes.length; i++) { if (i > 0) builder.add(", "); builder.add(subtypes[i]); + builder.add(".$L", writerMethod); } builder.add(")"); diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderAnnotatedClass.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderAnnotatedClass.java index 218a3e4c..d395ba41 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderAnnotatedClass.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderAnnotatedClass.java @@ -120,6 +120,7 @@ private void setFields(MethodSpec.Builder methodSpec, ProcessorContext context) typeVarMap, String.format("%s[$L].%s", ENCODERS_ARG, ENCODER_UNSAFE_ACCESSOR), String.format("%s[$L].%s", ENCODERS_ARG, WRITER_UNSAFE_ACCESSOR), + WRITER_UNSAFE_ACCESSOR, ENCODER_REGISTRY, SCALE_WRITER_REGISTRY); val scaleAnnotationParser = new ScaleAnnotationParser(context); diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcMethodProcessor.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcMethodProcessor.java index fabfcd76..e7bf2d2b 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcMethodProcessor.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcMethodProcessor.java @@ -127,6 +127,7 @@ private CodeBlock getRpcDecodeCodeBlock(TypeMirror resultType, String arg, Proce EMPTY_TYPE_VAR_MAP, String.format("%s[$L].%s", DECODERS_ARG, DECODER_UNSAFE_ACCESSOR), String.format("%s[$L].%s", DECODERS_ARG, READER_UNSAFE_ACCESSOR), + READER_UNSAFE_ACCESSOR, DECODER_REGISTRY, SCALE_READER_REGISTRY); @@ -184,6 +185,7 @@ private void processParameters(MethodSpec.Builder methodSpecBuilder, EMPTY_TYPE_VAR_MAP, String.format("%s[$L].%s", ENCODERS_ARG, ENCODER_UNSAFE_ACCESSOR), String.format("%s[$L].%s", ENCODERS_ARG, WRITER_UNSAFE_ACCESSOR), + WRITER_UNSAFE_ACCESSOR, ENCODER_REGISTRY, SCALE_WRITER_REGISTRY); @@ -199,7 +201,7 @@ private void processParameters(MethodSpec.Builder methodSpecBuilder, for (val param : method.getParameters()) { try { processParameter(methodSpecBuilder, method, param, encoderCompositor, writerCompositor, scaleAnnotationParser, context); - } catch (Exception e){ + } catch (Exception e) { throw new ProcessingException(e, param, e.getMessage()); } } diff --git a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderProcessorTests.java b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderProcessorTests.java index c01eae2e..cb7e1e4c 100644 --- a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderProcessorTests.java +++ b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/decoder/RpcDecoderProcessorTests.java @@ -8,6 +8,12 @@ import lombok.val; import org.junit.jupiter.api.Test; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -56,8 +62,20 @@ void compilesAndDecodes() { // TODO move this test out of the project val decoder = registry.resolve(TestDecodable.class) .inject(DecoderPair.of(registry.resolve(String.class), null)); - Object source = gson.fromJson("{a: 4, b: \"123\", c: \"some\"}", Object.class); - val expected = new TestDecodable<>(4, "123", "some"); + Object source = gson.fromJson("{\"a\":4,\"b\":\"123\",\"c\":\"some\"," + + "\"d\":[\"1\",\"2\"],\"e\":{\"a\":1,\"b\":2},\"f\":\"0x04000000\"," + + "\"g\":\"0x0c0500000002000000fdffffff\"}", + Object.class); + val expected = new TestDecodable<>(4, + "123", + "some", + Arrays.asList("1", "2"), + Stream.of( + new AbstractMap.SimpleEntry<>("a", 1), + new AbstractMap.SimpleEntry<>("b", 2)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), + 4, + Arrays.asList(5, 2, -3)); val actual = decoder.decode(source); diff --git a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderProcessorTests.java b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderProcessorTests.java index 0598a993..e51de859 100644 --- a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderProcessorTests.java +++ b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/encoder/RpcEncoderProcessorTests.java @@ -6,11 +6,15 @@ import com.strategyobject.substrateclient.rpc.core.EncoderPair; import com.strategyobject.substrateclient.rpc.core.RpcEncoder; import com.strategyobject.substrateclient.rpc.core.registries.RpcEncoderRegistry; -import com.strategyobject.substrateclient.scale.ScaleUtils; -import com.strategyobject.substrateclient.scale.writers.I32Writer; import lombok.val; import org.junit.jupiter.api.Test; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -68,9 +72,19 @@ void compilesAndDecodes() { // TODO move this test out of the project )), null)); - val source = new TestEncodable<>(4, "some", new TestEncodable.Subclass<>(123), true); + val source = new TestEncodable<>(4, + "some", + new TestEncodable.Subclass<>(123), + true, + Arrays.asList("a", "b"), + Stream.of( + new AbstractMap.SimpleEntry<>("a", 1), + new AbstractMap.SimpleEntry<>("b", 2)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), + Arrays.asList(2, 3)); val expected = gson.fromJson( - "{\"a\":\"" + ScaleUtils.toHexString(4, new I32Writer()) + "\", \"b\": \"some\", \"c\": {\"a\": 123}, \"d\": true}", + "{\"a\":\"0x04000000\",\"b\":\"some\",\"c\":{\"a\":123},\"d\":true," + + "\"e\":[\"a\",\"b\"],\"f\":{\"a\":1,\"b\":2},\"h\":\"0x080200000003000000\"}", Object.class); val actual = gson.fromJson( diff --git a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestDecodable.java b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestDecodable.java index 83940e47..b8495c84 100644 --- a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestDecodable.java +++ b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestDecodable.java @@ -1,24 +1,28 @@ package com.strategyobject.substrateclient.rpc.codegen.substitutes; import com.strategyobject.substrateclient.rpc.core.annotations.RpcDecoder; +import com.strategyobject.substrateclient.scale.annotations.Scale; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; +import java.util.Map; + @RpcDecoder @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class TestDecodable { - public int a; - public String b; - public T c; - - public TestDecodable() { - - } - - public TestDecodable(int a, String b, T c) { - this.a = a; - this.b = b; - this.c = c; - } + private int a; + private String b; + private T c; + private List d; + private Map e; + @Scale + private int f; + @Scale + private List g; } diff --git a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestEncodable.java b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestEncodable.java index f0f4c4ba..0c9b4ab2 100644 --- a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestEncodable.java +++ b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/substitutes/TestEncodable.java @@ -7,26 +7,23 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; +import java.util.Map; + @RpcEncoder @Getter @Setter +@AllArgsConstructor public class TestEncodable { @Scale - public int a; - public String b; - public T c; - public boolean d; - - public TestEncodable() { - - } - - public TestEncodable(int a, String b, T c, boolean d) { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } + private int a; + private String b; + private T c; + private boolean d; + private List e; + private Map f; + @Scale + private List h; @Getter @Setter diff --git a/rpc/rpc-core/src/main/java/com/strategyobject/substrateclient/rpc/core/RpcRegistryHelper.java b/rpc/rpc-core/src/main/java/com/strategyobject/substrateclient/rpc/core/RpcRegistryHelper.java new file mode 100644 index 00000000..d0f3fdd8 --- /dev/null +++ b/rpc/rpc-core/src/main/java/com/strategyobject/substrateclient/rpc/core/RpcRegistryHelper.java @@ -0,0 +1,32 @@ +package com.strategyobject.substrateclient.rpc.core; + +import com.strategyobject.substrateclient.rpc.core.registries.RpcDecoderRegistry; +import com.strategyobject.substrateclient.rpc.core.registries.RpcEncoderRegistry; +import lombok.val; + +public final class RpcRegistryHelper { + private RpcRegistryHelper() { + } + + @SuppressWarnings("unchecked") + public static RpcDecoder resolveAndInjectOrNull(Class clazz, DecoderPair... dependencies) { + val target = (RpcDecoder) RpcDecoderRegistry.getInstance().resolve(clazz); + + if (target == null) { + return null; + } + + return target.inject(dependencies); + } + + @SuppressWarnings("unchecked") + public static RpcEncoder resolveAndInjectOrNull(Class clazz, EncoderPair... dependencies) { + val target = (RpcEncoder) RpcEncoderRegistry.getInstance().resolve(clazz); + + if (target == null) { + return null; + } + + return target.inject(dependencies); + } +} diff --git a/rpc/rpc-sections/src/main/java/com/strategyobject/substrateclient/rpc/sections/State.java b/rpc/rpc-sections/src/main/java/com/strategyobject/substrateclient/rpc/sections/State.java index 2f4a00ad..189272db 100644 --- a/rpc/rpc-sections/src/main/java/com/strategyobject/substrateclient/rpc/sections/State.java +++ b/rpc/rpc-sections/src/main/java/com/strategyobject/substrateclient/rpc/sections/State.java @@ -2,10 +2,10 @@ import com.strategyobject.substrateclient.rpc.core.annotations.RpcCall; import com.strategyobject.substrateclient.rpc.core.annotations.RpcInterface; -import com.strategyobject.substrateclient.rpc.types.Metadata; -import com.strategyobject.substrateclient.rpc.types.RuntimeVersion; +import com.strategyobject.substrateclient.rpc.types.*; import com.strategyobject.substrateclient.scale.annotations.Scale; +import java.util.List; import java.util.concurrent.CompletableFuture; @RpcInterface(section = "state") @@ -16,4 +16,13 @@ public interface State { @RpcCall(method = "getMetadata") @Scale CompletableFuture getMetadata(); + + @RpcCall(method = "getKeys") + CompletableFuture> getKeys(StorageKey key); + + @RpcCall(method = "getStorage") + CompletableFuture getStorage(StorageKey key); + + @RpcCall(method = "queryStorageAt") + CompletableFuture> queryStorageAt(List keys); } diff --git a/rpc/rpc-sections/src/test/java/com/strategyobject/substrateclient/rpc/sections/StateTests.java b/rpc/rpc-sections/src/test/java/com/strategyobject/substrateclient/rpc/sections/StateTests.java index 9706b904..22a52e28 100644 --- a/rpc/rpc-sections/src/test/java/com/strategyobject/substrateclient/rpc/sections/StateTests.java +++ b/rpc/rpc-sections/src/test/java/com/strategyobject/substrateclient/rpc/sections/StateTests.java @@ -1,7 +1,9 @@ package com.strategyobject.substrateclient.rpc.sections; +import com.strategyobject.substrateclient.common.utils.HexConverter; import com.strategyobject.substrateclient.rpc.codegen.sections.RpcGeneratedSectionFactory; import com.strategyobject.substrateclient.rpc.codegen.sections.RpcInterfaceInitializationException; +import com.strategyobject.substrateclient.rpc.types.StorageKey; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; import com.strategyobject.substrateclient.transport.ws.WsProvider; @@ -11,11 +13,13 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; @Testcontainers public class StateTests { @@ -59,4 +63,68 @@ void getMetadata() throws ExecutionException, InterruptedException, TimeoutExcep }); } } + + @Test + void getKeys() throws ExecutionException, InterruptedException, TimeoutException, RpcInterfaceInitializationException { + try (WsProvider wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()) + .disableAutoConnect() + .build()) { + wsProvider.connect().get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + val sectionFactory = new RpcGeneratedSectionFactory(); + State rpcSection = sectionFactory.create(State.class, wsProvider); + + // xxhash128("Balances") = 0xc2261276cc9d1f8598ea4b6a74b15c2f + // xxhash128("StorageVersion") = 0x308ce9615de0775a82f8a94dc3d285a1 + val key = "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1"; // TODO implement and use `xxhash` + val keys = rpcSection.getKeys(StorageKey.valueOf(HexConverter.toBytes(key))).get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + assertTrue(keys.size() > 0); + } + } + + @Test + void getStorage() throws ExecutionException, InterruptedException, TimeoutException, RpcInterfaceInitializationException { + try (WsProvider wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()) + .disableAutoConnect() + .build()) { + wsProvider.connect().get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + val sectionFactory = new RpcGeneratedSectionFactory(); + State rpcSection = sectionFactory.create(State.class, wsProvider); + + // xxhash128("Balances") = 0xc2261276cc9d1f8598ea4b6a74b15c2f + // xxhash128("StorageVersion") = 0x308ce9615de0775a82f8a94dc3d285a1 + val key = "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1"; // TODO implement and use `xxhash` + val storageData = rpcSection.getStorage(StorageKey.valueOf(HexConverter.toBytes(key))).get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + assertTrue(storageData != null); + assertTrue(storageData.getData().length > 0); + } + } + + @Test + void getStorageAt() throws ExecutionException, InterruptedException, TimeoutException, RpcInterfaceInitializationException { + try (WsProvider wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()) + .disableAutoConnect() + .build()) { + wsProvider.connect().get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + val sectionFactory = new RpcGeneratedSectionFactory(); + State rpcSection = sectionFactory.create(State.class, wsProvider); + + // xxhash128("Balances") = 0xc2261276cc9d1f8598ea4b6a74b15c2f + // xxhash128("StorageVersion") = 0x308ce9615de0775a82f8a94dc3d285a1 + val key = "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1"; // TODO implement and use `xxhash` + val changes = rpcSection.queryStorageAt(Collections.singletonList(StorageKey.valueOf(HexConverter.toBytes(key)))).get(WAIT_TIMEOUT, TimeUnit.SECONDS); + + assertTrue(changes.size() > 0); + assertTrue(changes.get(0).getChanges().size() > 0); + assertTrue(changes.get(0).getChanges().get(0).getValue0().getData() != null); + assertTrue(changes.get(0).getChanges().get(0).getValue0().getData().length > 0); + } + } } diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/PairDecoder.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/PairDecoder.java new file mode 100644 index 00000000..0475c3d2 --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/PairDecoder.java @@ -0,0 +1,33 @@ +package com.strategyobject.substrateclient.rpc.types; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.rpc.core.DecoderPair; +import com.strategyobject.substrateclient.rpc.core.annotations.AutoRegister; +import com.strategyobject.substrateclient.rpc.core.decoders.AbstractDecoder; +import com.strategyobject.substrateclient.types.tuples.Pair; +import lombok.val; + +import java.util.List; + +@AutoRegister(types = Pair.class) +public class PairDecoder extends AbstractDecoder> { + @Override + protected Pair decodeNonNull(Object value, DecoderPair[] decoders) { + val firstDecoder = decoders[0].getDecoderOrThrow(); + val secondDecoder = decoders[1].getDecoderOrThrow(); + + val tuple = (List) value; + + return Pair.of( + firstDecoder.decode(tuple.get(0)), + secondDecoder.decode(tuple.get(1)) + ); + } + + @Override + protected void checkArguments(Object value, DecoderPair[] decoders) { + Preconditions.checkArgument(decoders != null && decoders.length == 2); + Preconditions.checkNotNull(decoders[0]); + Preconditions.checkNotNull(decoders[1]); + } +} diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageChangeSet.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageChangeSet.java new file mode 100644 index 00000000..92d5631f --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageChangeSet.java @@ -0,0 +1,20 @@ +package com.strategyobject.substrateclient.rpc.types; + +import com.strategyobject.substrateclient.rpc.core.annotations.RpcDecoder; +import com.strategyobject.substrateclient.scale.annotations.Scale; +import com.strategyobject.substrateclient.types.tuples.Pair; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@RpcDecoder +@NoArgsConstructor +@Getter +@Setter +public class StorageChangeSet { + @Scale + private BlockHash block; + private List> changes; +} diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageData.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageData.java new file mode 100644 index 00000000..d32483b0 --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageData.java @@ -0,0 +1,10 @@ +package com.strategyobject.substrateclient.rpc.types; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(staticName = "valueOf") +public class StorageData { + private final byte[] data; +} diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageDataDecoder.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageDataDecoder.java new file mode 100644 index 00000000..ad745b6f --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageDataDecoder.java @@ -0,0 +1,14 @@ +package com.strategyobject.substrateclient.rpc.types; + +import com.strategyobject.substrateclient.common.utils.HexConverter; +import com.strategyobject.substrateclient.rpc.core.DecoderPair; +import com.strategyobject.substrateclient.rpc.core.annotations.AutoRegister; +import com.strategyobject.substrateclient.rpc.core.decoders.AbstractDecoder; + +@AutoRegister(types = StorageData.class) +public class StorageDataDecoder extends AbstractDecoder { + @Override + protected StorageData decodeNonNull(Object value, DecoderPair[] decoders) { + return StorageData.valueOf(HexConverter.toBytes((String) value)); + } +} \ No newline at end of file diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKey.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKey.java new file mode 100644 index 00000000..2aa307d0 --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKey.java @@ -0,0 +1,10 @@ +package com.strategyobject.substrateclient.rpc.types; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(staticName = "valueOf") +public class StorageKey { + private final byte[] data; +} diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyDecoder.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyDecoder.java new file mode 100644 index 00000000..82fc7f78 --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyDecoder.java @@ -0,0 +1,14 @@ +package com.strategyobject.substrateclient.rpc.types; + +import com.strategyobject.substrateclient.common.utils.HexConverter; +import com.strategyobject.substrateclient.rpc.core.DecoderPair; +import com.strategyobject.substrateclient.rpc.core.annotations.AutoRegister; +import com.strategyobject.substrateclient.rpc.core.decoders.AbstractDecoder; + +@AutoRegister(types = StorageKey.class) +public class StorageKeyDecoder extends AbstractDecoder { + @Override + protected StorageKey decodeNonNull(Object value, DecoderPair[] decoders) { + return StorageKey.valueOf(HexConverter.toBytes((String) value)); + } +} diff --git a/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyEncoder.java b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyEncoder.java new file mode 100644 index 00000000..81272600 --- /dev/null +++ b/rpc/rpc-types/src/main/java/com/strategyobject/substrateclient/rpc/types/StorageKeyEncoder.java @@ -0,0 +1,18 @@ +package com.strategyobject.substrateclient.rpc.types; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.common.utils.HexConverter; +import com.strategyobject.substrateclient.rpc.core.EncoderPair; +import com.strategyobject.substrateclient.rpc.core.RpcEncoder; +import com.strategyobject.substrateclient.rpc.core.annotations.AutoRegister; +import lombok.NonNull; + +@AutoRegister(types = StorageKey.class) +public class StorageKeyEncoder implements RpcEncoder { + @Override + public Object encode(@NonNull StorageKey source, EncoderPair... encoders) { + Preconditions.checkArgument(encoders == null || encoders.length == 0); + + return HexConverter.toHex(source.getData()); + } +} diff --git a/scale/src/main/java/com/strategyobject/substrateclient/scale/ScaleRegistryHelper.java b/scale/src/main/java/com/strategyobject/substrateclient/scale/ScaleRegistryHelper.java new file mode 100644 index 00000000..612a5f26 --- /dev/null +++ b/scale/src/main/java/com/strategyobject/substrateclient/scale/ScaleRegistryHelper.java @@ -0,0 +1,32 @@ +package com.strategyobject.substrateclient.scale; + +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; +import lombok.val; + +public final class ScaleRegistryHelper { + private ScaleRegistryHelper() { + } + + @SuppressWarnings("unchecked") + public static ScaleReader resolveAndInjectOrNull(Class clazz, ScaleReader... dependencies) { + val target = (ScaleReader) ScaleReaderRegistry.getInstance().resolve(clazz); + + if (target == null) { + return null; + } + + return target.inject(dependencies); + } + + @SuppressWarnings("unchecked") + public static ScaleWriter resolveAndInjectOrNull(Class clazz, ScaleWriter... dependencies) { + val target = (ScaleWriter) ScaleWriterRegistry.getInstance().resolve(clazz); + + if (target == null) { + return null; + } + + return target.inject(dependencies); + } +} diff --git a/types/src/main/java/com/strategyobject/substrateclient/types/tuples/Pair.java b/types/src/main/java/com/strategyobject/substrateclient/types/tuples/Pair.java new file mode 100644 index 00000000..52fb6c04 --- /dev/null +++ b/types/src/main/java/com/strategyobject/substrateclient/types/tuples/Pair.java @@ -0,0 +1,11 @@ +package com.strategyobject.substrateclient.types.tuples; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(staticName = "of") +public final class Pair { + private final F value0; + private final S value1; +}