From a2ef95397c0299f7d1d592736dcdd07f66620528 Mon Sep 17 00:00:00 2001 From: Alexander Kalankhodzhaev Date: Fri, 8 Jul 2022 10:52:33 +0700 Subject: [PATCH] add MetadataProvider --- .../substrateclient/api/Api.java | 11 ++ .../substrateclient/api/DefaultModule.java | 103 +++++++++++++++--- .../substrateclient/api/RequireModule.java | 2 + .../api/{ApiTests.java => ApiTest.java} | 22 +++- .../common/types/AutoRegistry.java | 5 - .../crypto/ss58/AddressWithPrefix.java | 2 +- .../crypto/ss58/SS58AddressFormat.java | 37 +++++++ .../crypto/ss58/SS58Codec.java | 10 +- .../crypto/ss58/SS58CodecTests.java | 8 +- .../substrateclient/pallet/TestsHelper.java | 33 +++++- rpc/build.gradle | 1 + .../substrateclient/rpc/api/AccountId.java | 17 --- .../rpc/api/AccountIdEncoder.java | 11 +- .../rpc/api/ExtrinsicStatusDecoder.java | 8 +- .../rpc/api/section/TestsHelper.java | 33 +++++- .../decoder/RpcDecoderAnnotatedClass.java | 21 ++-- .../encoder/RpcEncoderAnnotatedClass.java | 16 ++- .../decoder/RpcDecoderProcessorTests.java | 10 +- .../encoder/RpcEncoderProcessorTests.java | 10 +- .../sections/RpcSectionFactoryTest.java | 22 ++-- .../rpc/context/RpcDecoderContext.java | 15 +++ .../rpc/context/RpcDecoderContextFactory.java | 5 + .../rpc/context/RpcEncoderContext.java | 15 +++ .../rpc/context/RpcEncoderContextFactory.java | 5 + .../rpc/metadata/ManualMetadataProvider.java | 20 ++++ .../rpc/metadata/MetadataProvider.java | 10 ++ .../substrateclient/rpc/metadata/Pallet.java | 12 ++ .../rpc/metadata/PalletCollection.java | 28 +++++ .../rpc/registries/RpcDecoderRegistry.java | 18 ++- .../rpc/registries/RpcEncoderRegistry.java | 18 ++- .../rpc/encoders/DispatchingEncoderTest.java | 3 +- .../rpc/metadata/PalletCollectionTest.java | 44 ++++++++ .../scale/registries/ScaleReaderRegistry.java | 3 +- .../scale/registries/ScaleWriterRegistry.java | 3 +- 34 files changed, 447 insertions(+), 134 deletions(-) rename api/src/test/java/com/strategyobject/substrateclient/api/{ApiTests.java => ApiTest.java} (85%) delete mode 100644 common/src/main/java/com/strategyobject/substrateclient/common/types/AutoRegistry.java create mode 100644 crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58AddressFormat.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContext.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContextFactory.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContext.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContextFactory.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/ManualMetadataProvider.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/MetadataProvider.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/Pallet.java create mode 100644 rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollection.java create mode 100644 rpc/src/test/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollectionTest.java diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/Api.java b/api/src/main/java/com/strategyobject/substrateclient/api/Api.java index 8516008d..8f8106d1 100644 --- a/api/src/main/java/com/strategyobject/substrateclient/api/Api.java +++ b/api/src/main/java/com/strategyobject/substrateclient/api/Api.java @@ -3,6 +3,7 @@ import com.google.inject.Module; import com.strategyobject.substrateclient.pallet.PalletFactory; import com.strategyobject.substrateclient.rpc.RpcSectionFactory; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; import com.strategyobject.substrateclient.transport.ProviderInterface; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -20,6 +21,7 @@ public class Api implements AutoCloseable { private final @NonNull RpcSectionFactory rpcSectionFactory; private final @NonNull PalletFactory palletFactory; + private final @NonNull MetadataProvider metadataProvider; private final Map, Object> resolvedCache = new ConcurrentHashMap<>(); @@ -45,6 +47,15 @@ public T pallet(@NonNull Class clazz) { return clazz.cast(resolvedCache.computeIfAbsent(clazz, palletFactory::create)); } + /** + * Provides access to current version of metadata in use. + * + * @return MetadataProvider instance + */ + public MetadataProvider metadata() { + return metadataProvider; + } + @Override public void close() throws Exception { if (rpcSectionFactory instanceof AutoCloseable) { diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java b/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java index 4441f96d..81153c84 100644 --- a/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java +++ b/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java @@ -3,12 +3,20 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; -import com.strategyobject.substrateclient.common.types.AutoRegistry; +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.pallet.GeneratedPalletFactory; import com.strategyobject.substrateclient.pallet.PalletFactory; import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory; import com.strategyobject.substrateclient.rpc.RpcSectionFactory; import com.strategyobject.substrateclient.rpc.api.section.State; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContextFactory; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContextFactory; +import com.strategyobject.substrateclient.rpc.metadata.ManualMetadataProvider; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; +import com.strategyobject.substrateclient.rpc.metadata.Pallet; +import com.strategyobject.substrateclient.rpc.metadata.PalletCollection; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; @@ -18,20 +26,21 @@ import lombok.RequiredArgsConstructor; import lombok.val; +import java.util.function.BiConsumer; import java.util.function.Consumer; @RequiredArgsConstructor public class DefaultModule extends AbstractModule { - private final @NonNull ProviderInterface providerInterface; + private static final String PREFIX = "com.strategyobject.substrateclient"; - private Consumer configureScaleReaderRegistry = this::autoRegister; - private Consumer configureScaleWriterRegistry = this::autoRegister; - private Consumer configureRpcDecoderRegistry = this::autoRegister; - private Consumer configureRpcEncoderRegistry = this::autoRegister; + private final @NonNull ProviderInterface providerInterface; - private void autoRegister(AutoRegistry registry) { - registry.registerAnnotatedFrom("com.strategyobject.substrateclient"); - } + private Consumer configureScaleReaderRegistry = x -> x.registerAnnotatedFrom(PREFIX); + private Consumer configureScaleWriterRegistry = x -> x.registerAnnotatedFrom(PREFIX); + private BiConsumer configureRpcDecoderRegistry = + (registry, contextFactory) -> registry.registerAnnotatedFrom(contextFactory, PREFIX); + private BiConsumer configureRpcEncoderRegistry = + (registry, contextFactory) -> registry.registerAnnotatedFrom(contextFactory, PREFIX); public DefaultModule configureScaleReaderRegistry(Consumer configure) { configureScaleReaderRegistry = configureScaleReaderRegistry.andThen(configure); @@ -43,12 +52,12 @@ public DefaultModule configureScaleWriterRegistry(Consumer return this; } - public DefaultModule configureRpcDecoderRegistry(Consumer configure) { + public DefaultModule configureRpcDecoderRegistry(BiConsumer configure) { configureRpcDecoderRegistry = configureRpcDecoderRegistry.andThen(configure); return this; } - public DefaultModule configureRpcEncoderRegistry(Consumer configure) { + public DefaultModule configureRpcEncoderRegistry(BiConsumer configure) { configureRpcEncoderRegistry = configureRpcEncoderRegistry.andThen(configure); return this; } @@ -78,7 +87,8 @@ protected void configure() { .toConstructor( Api.class.getConstructor( RpcSectionFactory.class, - PalletFactory.class)) + PalletFactory.class, + MetadataProvider.class)) .asEagerSingleton(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); @@ -103,20 +113,77 @@ public ScaleWriterRegistry provideScaleWriterRegistry() { @Provides @Singleton - public RpcDecoderRegistry provideRpcDecoderRegistry(ScaleReaderRegistry scaleReaderRegistry) { - val registry = new RpcDecoderRegistry(scaleReaderRegistry); - configureRpcDecoderRegistry.accept(registry); + public RpcDecoderRegistry provideRpcDecoderRegistry(MetadataProvider metadataProvider, + ScaleReaderRegistry scaleReaderRegistry) { + val registry = new RpcDecoderRegistry(); + val context = new RpcDecoderContext( + metadataProvider, + registry, + scaleReaderRegistry); + + configureRpcDecoderRegistry.accept(registry, () -> context); return registry; } @Provides @Singleton - public RpcEncoderRegistry provideRpcEncoderRegistry(ScaleWriterRegistry scaleWriterRegistry) { - val registry = new RpcEncoderRegistry(scaleWriterRegistry); - configureRpcEncoderRegistry.accept(registry); + public RpcEncoderRegistry provideRpcEncoderRegistry(MetadataProvider metadataProvider, + ScaleWriterRegistry scaleWriterRegistry) { + val registry = new RpcEncoderRegistry(); + val context = new RpcEncoderContext( + metadataProvider, + registry, + scaleWriterRegistry); + + configureRpcEncoderRegistry.accept(registry, () -> context); return registry; } + @Provides + @Singleton + public MetadataProvider provideMetadata() { + // TODO. Use provider based on real Metadata + return new ManualMetadataProvider( + SS58AddressFormat.SUBSTRATE_ACCOUNT, + new PalletCollection( + new Pallet(0, "System"), + new Pallet(1, "Utility"), + new Pallet(2, "Babe"), + new Pallet(3, "Timestamp"), + new Pallet(4, "Authorship"), + new Pallet(5, "Indices"), + new Pallet(6, "Balances"), + new Pallet(7, "TransactionPayment"), + new Pallet(8, "Staking"), + new Pallet(9, "Session"), + new Pallet(10, "Democracy"), + new Pallet(11, "Council"), + new Pallet(12, "TechnicalCommittee"), + new Pallet(13, "Elections"), + new Pallet(14, "TechnicalMembership"), + new Pallet(15, "Grandpa"), + new Pallet(16, "Treasury"), + new Pallet(17, "Contracts"), + new Pallet(18, "Sudo"), + new Pallet(19, "ImOnline"), + new Pallet(20, "AuthorityDiscovery"), + new Pallet(21, "Offences"), + new Pallet(22, "Historical"), + new Pallet(23, "RandomnessCollectiveFlip"), + new Pallet(24, "Identity"), + new Pallet(25, "Society"), + new Pallet(26, "Recovery"), + new Pallet(27, "Vesting"), + new Pallet(28, "Scheduler"), + new Pallet(29, "Proxy"), + new Pallet(30, "Multisig"), + new Pallet(31, "Bounties"), + new Pallet(32, "Tips"), + new Pallet(33, "Assets"), + new Pallet(34, "Mmr"), + new Pallet(35, "Lottery"))); + } + @Provides @Singleton public State provideState(RpcSectionFactory rpcSectionFactory) { diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/RequireModule.java b/api/src/main/java/com/strategyobject/substrateclient/api/RequireModule.java index 955429e9..21884ecb 100644 --- a/api/src/main/java/com/strategyobject/substrateclient/api/RequireModule.java +++ b/api/src/main/java/com/strategyobject/substrateclient/api/RequireModule.java @@ -3,6 +3,7 @@ import com.google.inject.AbstractModule; import com.strategyobject.substrateclient.pallet.PalletFactory; import com.strategyobject.substrateclient.rpc.RpcSectionFactory; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; @@ -17,6 +18,7 @@ protected void configure() { requireBinding(ScaleWriterRegistry.class); requireBinding(RpcDecoderRegistry.class); requireBinding(RpcEncoderRegistry.class); + requireBinding(MetadataProvider.class); requireBinding(RpcSectionFactory.class); requireBinding(PalletFactory.class); requireBinding(Api.class); diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/ApiTests.java b/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java similarity index 85% rename from api/src/test/java/com/strategyobject/substrateclient/api/ApiTests.java rename to api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java index 85ab6973..f9bbcf02 100644 --- a/api/src/test/java/com/strategyobject/substrateclient/api/ApiTests.java +++ b/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java @@ -3,6 +3,7 @@ import com.google.inject.CreationException; import com.google.inject.util.Modules; import com.strategyobject.substrateclient.common.convert.HexConverter; +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.pallet.PalletFactory; import com.strategyobject.substrateclient.rpc.api.AccountId; import com.strategyobject.substrateclient.rpc.api.BlockNumber; @@ -24,7 +25,7 @@ import static org.mockito.Mockito.verify; @Testcontainers -class ApiTests { +class ApiTest { private static final int WAIT_TIMEOUT = 1000; @Container @@ -61,6 +62,19 @@ void getSystemSectionAndCall() throws Exception { } } + @Test + void getSS58AddressFormat() throws Exception { + val wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()); + + try (val api = Api.with(wsProvider).build().join()) { + val ss58AddressFormat = api.metadata().getSS58AddressFormat(); + + assertNotNull(ss58AddressFormat); + assertEquals(SS58AddressFormat.SUBSTRATE_ACCOUNT, ss58AddressFormat); + } + } + @Test void configureApi() throws Exception { val wsProvider = WsProvider.builder() @@ -69,10 +83,8 @@ void configureApi() throws Exception { val expected = mock(Index.class); try (val api = Api.with(wsProvider) .configure(defaultModule -> - defaultModule.configureRpcDecoderRegistry(registry -> - registry.register( - (value, decoders) -> expected, - Index.class))) + defaultModule.configureRpcDecoderRegistry((registry, _factory) -> + registry.register((value, decoders) -> expected, Index.class))) .build() .join()) { val system = api.rpc(System.class); diff --git a/common/src/main/java/com/strategyobject/substrateclient/common/types/AutoRegistry.java b/common/src/main/java/com/strategyobject/substrateclient/common/types/AutoRegistry.java deleted file mode 100644 index 10afd8d1..00000000 --- a/common/src/main/java/com/strategyobject/substrateclient/common/types/AutoRegistry.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.strategyobject.substrateclient.common.types; - -public interface AutoRegistry { - void registerAnnotatedFrom(String... prefixes); -} diff --git a/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/AddressWithPrefix.java b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/AddressWithPrefix.java index 47b23c1d..eba52c47 100644 --- a/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/AddressWithPrefix.java +++ b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/AddressWithPrefix.java @@ -10,5 +10,5 @@ @Getter public class AddressWithPrefix { private final byte @NonNull [] address; - private final short prefix; + private final SS58AddressFormat prefix; } diff --git a/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58AddressFormat.java b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58AddressFormat.java new file mode 100644 index 00000000..082ecee9 --- /dev/null +++ b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58AddressFormat.java @@ -0,0 +1,37 @@ +package com.strategyobject.substrateclient.crypto.ss58; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(staticName = "of") +@EqualsAndHashCode +public class SS58AddressFormat { + private final short prefix; + + /** + * Polkadot Relay-chain, standard account (*25519). + */ + public static final SS58AddressFormat POLKADOT_ACCOUNT = new SS58AddressFormat((short) 0); + + /** + * Bare 32-bit Schnorr/Ristretto 25519 (S/R 25519) key. + */ + public static final SS58AddressFormat BARE_SR_25519 = new SS58AddressFormat((short) 1); + + /** + * Kusama Relay-chain, standard account (*25519). + */ + public static final SS58AddressFormat KUSAMA_ACCOUNT = new SS58AddressFormat((short) 2); + + /** + * Bare 32-bit Edwards Ed25519 key. + */ + public static final SS58AddressFormat BARE_ED_25519 = new SS58AddressFormat((short) 3); + + /** + * Any Substrate network, standard account (*25519). + */ + public static final SS58AddressFormat SUBSTRATE_ACCOUNT = new SS58AddressFormat((short) 42); +} diff --git a/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58Codec.java b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58Codec.java index 82dad5e2..c7a7f5f1 100644 --- a/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58Codec.java +++ b/crypto/src/main/java/com/strategyobject/substrateclient/crypto/ss58/SS58Codec.java @@ -53,15 +53,17 @@ public static AddressWithPrefix decode(@NonNull String encoded) { throw new IllegalArgumentException("Incorrect checksum."); } - return AddressWithPrefix.from(Arrays.copyOfRange(data, typeLen, typeLen + ADDRESS_LENGTH), prefix); + return AddressWithPrefix.from( + Arrays.copyOfRange(data, typeLen, typeLen + ADDRESS_LENGTH), + SS58AddressFormat.of(prefix)); } - public static String encode(byte @NonNull [] address, short prefix) { + public static String encode(byte @NonNull [] address, SS58AddressFormat prefix) { Preconditions.checkArgument(address.length == ADDRESS_LENGTH, "The length of address must be 32, but was: " + address.length); - val ident = prefix & 0b0011_1111_1111_1111; - Preconditions.checkArgument(ident == prefix, + val ident = prefix.getPrefix() & 0b0011_1111_1111_1111; + Preconditions.checkArgument(ident == prefix.getPrefix(), "The prefix size is restricted by 14 bits."); byte[] data; diff --git a/crypto/src/test/java/com/strategyobject/substrateclient/crypto/ss58/SS58CodecTests.java b/crypto/src/test/java/com/strategyobject/substrateclient/crypto/ss58/SS58CodecTests.java index 77477f90..b3ad967a 100644 --- a/crypto/src/test/java/com/strategyobject/substrateclient/crypto/ss58/SS58CodecTests.java +++ b/crypto/src/test/java/com/strategyobject/substrateclient/crypto/ss58/SS58CodecTests.java @@ -20,7 +20,7 @@ class SS58CodecTests { }, delimiterString = ":") void encode(String expected, String hex, short prefix) { - val actual = SS58Codec.encode(HexConverter.toBytes(hex), prefix); + val actual = SS58Codec.encode(HexConverter.toBytes(hex), SS58AddressFormat.of(prefix)); assertEquals(expected, actual); } @@ -33,7 +33,9 @@ void encode(String expected, String hex, short prefix) { }, delimiterString = ":") void encodeThrows(String hex, short prefix) { - assertThrows(IllegalArgumentException.class, () -> SS58Codec.encode(HexConverter.toBytes(hex), prefix)); + val format = SS58AddressFormat.of(prefix); + val address = HexConverter.toBytes(hex); + assertThrows(IllegalArgumentException.class, () -> SS58Codec.encode(address, format)); } @ParameterizedTest @@ -49,7 +51,7 @@ void encodeThrows(String hex, short prefix) { void decode(String source, String hex, short prefix) { val actual = SS58Codec.decode(source); - val expected = AddressWithPrefix.from(HexConverter.toBytes(hex), prefix); + val expected = AddressWithPrefix.from(HexConverter.toBytes(hex), SS58AddressFormat.of(prefix)); assertEquals(expected, actual); } diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/TestsHelper.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/TestsHelper.java index 8081974d..99cfe6fc 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/TestsHelper.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/TestsHelper.java @@ -1,13 +1,26 @@ package com.strategyobject.substrateclient.pallet; +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; import com.strategyobject.substrateclient.transport.ProviderInterface; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class TestsHelper { + public static final MetadataProvider METADATA_PROVIDER = mock(MetadataProvider.class); + + static { + when(METADATA_PROVIDER.getSS58AddressFormat()).thenReturn(SS58AddressFormat.SUBSTRATE_ACCOUNT); + } + public static final ScaleReaderRegistry SCALE_READER_REGISTRY = new ScaleReaderRegistry() {{ registerAnnotatedFrom("com.strategyobject.substrateclient"); }}; @@ -16,13 +29,21 @@ public class TestsHelper { registerAnnotatedFrom("com.strategyobject.substrateclient"); }}; - public static final RpcEncoderRegistry RPC_ENCODER_REGISTRY = new RpcEncoderRegistry(SCALE_WRITER_REGISTRY) {{ - registerAnnotatedFrom("com.strategyobject.substrateclient"); - }}; + public static final RpcEncoderRegistry RPC_ENCODER_REGISTRY = new RpcEncoderRegistry(); - public static final RpcDecoderRegistry RPC_DECODER_REGISTRY = new RpcDecoderRegistry(SCALE_READER_REGISTRY) {{ - registerAnnotatedFrom("com.strategyobject.substrateclient"); - }}; + static { + RPC_ENCODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcEncoderContext(METADATA_PROVIDER, RPC_ENCODER_REGISTRY, SCALE_WRITER_REGISTRY), + "com.strategyobject.substrateclient"); + } + + public static final RpcDecoderRegistry RPC_DECODER_REGISTRY = new RpcDecoderRegistry(); + + static { + RPC_DECODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcDecoderContext(METADATA_PROVIDER, RPC_DECODER_REGISTRY, SCALE_READER_REGISTRY), + "com.strategyobject.substrateclient"); + } public static GeneratedRpcSectionFactory createSectionFactory(ProviderInterface provider) { return new GeneratedRpcSectionFactory( diff --git a/rpc/build.gradle b/rpc/build.gradle index dccf5499..f1414121 100644 --- a/rpc/build.gradle +++ b/rpc/build.gradle @@ -1,5 +1,6 @@ dependencies { implementation project(':common') + implementation project(':crypto') implementation project(':scale') implementation project(':transport') diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountId.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountId.java index a467f1db..c048a954 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountId.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountId.java @@ -2,31 +2,14 @@ import com.strategyobject.substrateclient.common.types.FixedBytes; import com.strategyobject.substrateclient.common.types.Size; -import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import lombok.NonNull; -import lombok.var; - -import java.util.concurrent.atomic.AtomicReference; public class AccountId extends FixedBytes { - private final AtomicReference encoded = new AtomicReference<>(null); - protected AccountId(byte[] data) { super(data, Size.of32); } - @Override - public String toString() { - var result = encoded.get(); - if (result != null) { - return result; - } - - result = SS58Codec.encode(getBytes(), MetadataRegistry.getInstance().getSS58AddressFormat()); - return encoded.compareAndSet(null, result) ? result : encoded.get(); - } - public static AccountId fromBytes(byte @NonNull [] data) { return new AccountId(data); } diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountIdEncoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountIdEncoder.java index 554c8ade..2caf6528 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountIdEncoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AccountIdEncoder.java @@ -1,16 +1,25 @@ package com.strategyobject.substrateclient.rpc.api; import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import com.strategyobject.substrateclient.rpc.EncoderPair; import com.strategyobject.substrateclient.rpc.RpcEncoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @AutoRegister(types = AccountId.class) public class AccountIdEncoder implements RpcEncoder { + private final @NonNull RpcEncoderContext rpcEncoderContext; + @Override public Object encode(AccountId source, EncoderPair... encoders) { Preconditions.checkArgument(encoders == null || encoders.length == 0); - return source.toString(); + return SS58Codec.encode( + source.getBytes(), + rpcEncoderContext.getMetadataProvider().getSS58AddressFormat()); } } diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatusDecoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatusDecoder.java index d3ff7b4e..52437ecf 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatusDecoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatusDecoder.java @@ -2,9 +2,8 @@ import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; import com.strategyobject.substrateclient.rpc.decoders.AbstractDecoder; -import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; -import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; import com.strategyobject.substrateclient.transport.RpcObject; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -19,8 +18,7 @@ public class ExtrinsicStatusDecoder extends AbstractDecoder { private static final Map STATUS_TO_VALUE = new HashMap<>(); private static final Map> STATUS_TO_CLASS = new HashMap<>(); - private final @NonNull RpcDecoderRegistry decoderRegistry; - private final @NonNull ScaleReaderRegistry scaleReaderRegistry; + private final @NonNull RpcDecoderContext rpcDecoderContext; static { STATUS_TO_VALUE.put("future", ExtrinsicStatus.Future); @@ -46,7 +44,7 @@ protected ExtrinsicStatus decodeNonNull(RpcObject value, DecoderPair[] decode .stream() .filter(e -> STATUS_TO_CLASS.containsKey(e.getKey())) .findFirst() - .map(e -> (ExtrinsicStatus) decoderRegistry.resolve(STATUS_TO_CLASS.get(e.getKey())).decode(value)); + .map(e -> (ExtrinsicStatus) rpcDecoderContext.getRpcDecoderRegistry().resolve(STATUS_TO_CLASS.get(e.getKey())).decode(value)); } else { decoded = Optional.empty(); } diff --git a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/TestsHelper.java b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/TestsHelper.java index 2e4e0032..673bcee0 100644 --- a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/TestsHelper.java +++ b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/TestsHelper.java @@ -1,13 +1,26 @@ package com.strategyobject.substrateclient.rpc.api.section; +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; import com.strategyobject.substrateclient.transport.ProviderInterface; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class TestsHelper { + static final MetadataProvider METADATA_PROVIDER = mock(MetadataProvider.class); + + static { + when(METADATA_PROVIDER.getSS58AddressFormat()).thenReturn(SS58AddressFormat.SUBSTRATE_ACCOUNT); + } + static final ScaleReaderRegistry SCALE_READER_REGISTRY = new ScaleReaderRegistry() {{ registerAnnotatedFrom("com.strategyobject.substrateclient"); }}; @@ -16,13 +29,21 @@ public class TestsHelper { registerAnnotatedFrom("com.strategyobject.substrateclient"); }}; - static final RpcEncoderRegistry RPC_ENCODER_REGISTRY = new RpcEncoderRegistry(SCALE_WRITER_REGISTRY) {{ - registerAnnotatedFrom("com.strategyobject.substrateclient"); - }}; + static final RpcEncoderRegistry RPC_ENCODER_REGISTRY = new RpcEncoderRegistry(); - static final RpcDecoderRegistry RPC_DECODER_REGISTRY = new RpcDecoderRegistry(SCALE_READER_REGISTRY) {{ - registerAnnotatedFrom("com.strategyobject.substrateclient"); - }}; + static { + RPC_ENCODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcEncoderContext(METADATA_PROVIDER, RPC_ENCODER_REGISTRY, SCALE_WRITER_REGISTRY), + "com.strategyobject.substrateclient"); + } + + static final RpcDecoderRegistry RPC_DECODER_REGISTRY = new RpcDecoderRegistry(); + + static { + RPC_DECODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcDecoderContext(METADATA_PROVIDER, RPC_DECODER_REGISTRY, SCALE_READER_REGISTRY), + "com.strategyobject.substrateclient"); + } static GeneratedRpcSectionFactory createSectionFactory(ProviderInterface provider) { return new GeneratedRpcSectionFactory( 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 a485b75e..f1cd5743 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 @@ -1,11 +1,15 @@ package com.strategyobject.substrateclient.rpc.codegen.decoder; import com.squareup.javapoet.*; -import com.strategyobject.substrateclient.common.codegen.*; +import com.strategyobject.substrateclient.common.codegen.AnnotationUtils; +import com.strategyobject.substrateclient.common.codegen.JavaPoet; +import com.strategyobject.substrateclient.common.codegen.ProcessingException; +import com.strategyobject.substrateclient.common.codegen.ProcessorContext; import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.RpcDecoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; import com.strategyobject.substrateclient.rpc.annotation.Ignore; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.scale.ScaleReader; import com.strategyobject.substrateclient.scale.ScaleUtils; @@ -41,6 +45,7 @@ public class RpcDecoderAnnotatedClass { private static final String VALUE_ARG = "value"; private static final String RESULT_VAR = "result"; private static final String MAP_VAR = "sourceMap"; + private static final String CONTEXT = "context"; private final TypeElement classElement; private final Map typeVarMap; @@ -102,16 +107,12 @@ private MethodSpec generateDecodeMethod(ProcessorContext context, TypeName class private TypeSpec.Builder injectDependencies(TypeSpec.Builder typeSpecBuilder) { val ctor = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - .addParameter(RpcDecoderRegistry.class, DECODER_REGISTRY) - .addParameter(ScaleReaderRegistry.class, SCALE_READER_REGISTRY) - .beginControlFlow("if ($L == null)", DECODER_REGISTRY) - .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, DECODER_REGISTRY) + .addParameter(RpcDecoderContext.class, CONTEXT) + .beginControlFlow("if ($L == null)", CONTEXT) + .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, CONTEXT) .endControlFlow() - .addStatement("this.$1L = $1L", DECODER_REGISTRY) - .beginControlFlow("if ($L == null)", SCALE_READER_REGISTRY) - .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, SCALE_READER_REGISTRY) - .endControlFlow() - .addStatement("this.$1L = $1L", SCALE_READER_REGISTRY) + .addStatement("this.$L = $L.getRpcDecoderRegistry()", DECODER_REGISTRY, CONTEXT) + .addStatement("this.$L = $L.getScaleReaderRegistry()", SCALE_READER_REGISTRY, CONTEXT) .build(); return typeSpecBuilder 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 74fcdc74..9d4ea9aa 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 @@ -9,6 +9,7 @@ import com.strategyobject.substrateclient.rpc.RpcEncoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; import com.strategyobject.substrateclient.rpc.annotation.Ignore; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.ScaleUtils; import com.strategyobject.substrateclient.scale.ScaleWriter; @@ -45,6 +46,7 @@ public class RpcEncoderAnnotatedClass { private static final String ENCODER_NAME_TEMPLATE = "%sEncoder"; private static final String SOURCE_ARG = "source"; private static final String RESULT_VAR = "result"; + private static final String CONTEXT = "context"; private final TypeElement classElement; private final Map typeVarMap; private final List fields; @@ -84,16 +86,12 @@ public void generateEncoder(@NonNull ProcessorContext context) throws Processing private TypeSpec.Builder injectDependencies(TypeSpec.Builder typeSpecBuilder) { val ctor = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) - .addParameter(RpcEncoderRegistry.class, ENCODER_REGISTRY) - .addParameter(ScaleWriterRegistry.class, SCALE_WRITER_REGISTRY) - .beginControlFlow("if ($L == null)", ENCODER_REGISTRY) - .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, ENCODER_REGISTRY) + .addParameter(RpcEncoderContext.class, CONTEXT) + .beginControlFlow("if ($L == null)", CONTEXT) + .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, CONTEXT) .endControlFlow() - .addStatement("this.$1L = $1L", ENCODER_REGISTRY) - .beginControlFlow("if ($L == null)", SCALE_WRITER_REGISTRY) - .addStatement("throw new $T(\"$L can't be null.\")", IllegalArgumentException.class, SCALE_WRITER_REGISTRY) - .endControlFlow() - .addStatement("this.$1L = $1L", SCALE_WRITER_REGISTRY) + .addStatement("this.$L = $L.getRpcEncoderRegistry()", ENCODER_REGISTRY, CONTEXT) + .addStatement("this.$L = $L.getScaleWriterRegistry()", SCALE_WRITER_REGISTRY, CONTEXT) .build(); return typeSpecBuilder 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 08e96ee5..95f3fb6c 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 @@ -4,6 +4,7 @@ import com.google.testing.compile.JavaFileObjects; import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.codegen.substitutes.TestDecodable; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; import com.strategyobject.substrateclient.transport.RpcObject; @@ -16,6 +17,8 @@ import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class RpcDecoderProcessorTests { private final Gson gson = new Gson(); @@ -57,8 +60,11 @@ void compiles() { @Test void compilesAndDecodes() { // TODO move this test out of the project - val registry = new RpcDecoderRegistry(new ScaleReaderRegistry()); - registry.registerAnnotatedFrom("com.strategyobject.substrateclient.rpc.codegen.substitutes"); + val registry = new RpcDecoderRegistry(); + val context = mock(RpcDecoderContext.class); + when(context.getRpcDecoderRegistry()).thenReturn(registry); + when(context.getScaleReaderRegistry()).thenReturn(new ScaleReaderRegistry()); + registry.registerAnnotatedFrom(() -> context, "com.strategyobject.substrateclient.rpc.codegen.substitutes"); val decoder = registry.resolve(TestDecodable.class) .inject(DecoderPair.of(registry.resolve(String.class), null)); 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 83be1af3..74048858 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 @@ -5,6 +5,7 @@ import com.strategyobject.substrateclient.rpc.EncoderPair; import com.strategyobject.substrateclient.rpc.RpcEncoder; import com.strategyobject.substrateclient.rpc.codegen.substitutes.TestEncodable; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; import lombok.val; @@ -19,6 +20,8 @@ import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class RpcEncoderProcessorTests { private final Gson gson = new Gson(); @@ -61,8 +64,11 @@ void compiles() { @Test @SuppressWarnings("unchecked") void compilesAndDecodes() { // TODO move this test out of the project - val registry = new RpcEncoderRegistry(new ScaleWriterRegistry()); - registry.registerAnnotatedFrom("com.strategyobject.substrateclient.rpc.codegen.substitutes"); + val registry = new RpcEncoderRegistry(); + val context = mock(RpcEncoderContext.class); + when(context.getRpcEncoderRegistry()).thenReturn(registry); + when(context.getScaleWriterRegistry()).thenReturn(new ScaleWriterRegistry()); + registry.registerAnnotatedFrom(() -> context, "com.strategyobject.substrateclient.rpc.codegen.substitutes"); val encoder = (RpcEncoder>) registry.resolve(TestEncodable.class) .inject( diff --git a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSectionFactoryTest.java b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSectionFactoryTest.java index e1d07f16..4ba1318b 100644 --- a/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSectionFactoryTest.java +++ b/rpc/rpc-codegen/src/test/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSectionFactoryTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.Test; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyList; @@ -24,24 +23,23 @@ class RpcSectionFactoryTest { // TODO move this test out of the project @Test - void createsRpcSectionAndCallsMethod() throws ExecutionException, InterruptedException { + void createsRpcSectionAndCallsMethod() throws Exception { val expected = true; - val sendFuture = CompletableFuture.completedFuture((RpcObject) new RpcBoolean(expected)); val provider = mock(ProviderInterface.class); - when(provider.send(anyString(), anyList())) - .thenReturn(sendFuture); + when(provider.send(anyString(), anyList())).thenReturn(sendFuture); - val rpcGeneratedSectionFactory = new GeneratedRpcSectionFactory( + try (val rpcGeneratedSectionFactory = new GeneratedRpcSectionFactory( provider, - new RpcEncoderRegistry(new ScaleWriterRegistry()), + new RpcEncoderRegistry(), new ScaleWriterRegistry(), - new RpcDecoderRegistry(new ScaleReaderRegistry()), - new ScaleReaderRegistry()); - val rpcSection = rpcGeneratedSectionFactory.create(TestSection.class); + new RpcDecoderRegistry(), + new ScaleReaderRegistry())) { + val rpcSection = rpcGeneratedSectionFactory.create(TestSection.class); - val actual = rpcSection.doNothing("some").get(); + val actual = rpcSection.doNothing("some").get(); - assertEquals(expected, actual); + assertEquals(expected, actual); + } } } diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContext.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContext.java new file mode 100644 index 00000000..39d99c65 --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContext.java @@ -0,0 +1,15 @@ +package com.strategyobject.substrateclient.rpc.context; + +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; +import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class RpcDecoderContext { + private final MetadataProvider metadataProvider; + private final RpcDecoderRegistry rpcDecoderRegistry; + private final ScaleReaderRegistry scaleReaderRegistry; +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContextFactory.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContextFactory.java new file mode 100644 index 00000000..0217c59d --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcDecoderContextFactory.java @@ -0,0 +1,5 @@ +package com.strategyobject.substrateclient.rpc.context; + +public interface RpcDecoderContextFactory { + RpcDecoderContext create(); +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContext.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContext.java new file mode 100644 index 00000000..cd4353c6 --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContext.java @@ -0,0 +1,15 @@ +package com.strategyobject.substrateclient.rpc.context; + +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; +import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; +import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class RpcEncoderContext { + private final MetadataProvider metadataProvider; + private final RpcEncoderRegistry rpcEncoderRegistry; + private final ScaleWriterRegistry scaleWriterRegistry; +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContextFactory.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContextFactory.java new file mode 100644 index 00000000..90c935f4 --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/context/RpcEncoderContextFactory.java @@ -0,0 +1,5 @@ +package com.strategyobject.substrateclient.rpc.context; + +public interface RpcEncoderContextFactory { + RpcEncoderContext create(); +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/ManualMetadataProvider.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/ManualMetadataProvider.java new file mode 100644 index 00000000..d47a7567 --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/ManualMetadataProvider.java @@ -0,0 +1,20 @@ +package com.strategyobject.substrateclient.rpc.metadata; + +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ManualMetadataProvider implements MetadataProvider { + private final SS58AddressFormat ss58AddressFormat; + private final PalletCollection palletCollection; + + @Override + public SS58AddressFormat getSS58AddressFormat() { + return ss58AddressFormat; + } + + @Override + public PalletCollection getPallets() { + return palletCollection; + } +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/MetadataProvider.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/MetadataProvider.java new file mode 100644 index 00000000..acad1ddf --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/MetadataProvider.java @@ -0,0 +1,10 @@ +package com.strategyobject.substrateclient.rpc.metadata; + + +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; + +public interface MetadataProvider { + SS58AddressFormat getSS58AddressFormat(); + + PalletCollection getPallets(); +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/Pallet.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/Pallet.java new file mode 100644 index 00000000..a4056cd7 --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/Pallet.java @@ -0,0 +1,12 @@ +package com.strategyobject.substrateclient.rpc.metadata; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; + +@Getter +@AllArgsConstructor +public class Pallet { + private final int index; + private final @NonNull String name; +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollection.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollection.java new file mode 100644 index 00000000..02733e5e --- /dev/null +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollection.java @@ -0,0 +1,28 @@ +package com.strategyobject.substrateclient.rpc.metadata; + +import com.google.common.base.Preconditions; +import lombok.NonNull; + +import java.util.stream.IntStream; + +public class PalletCollection { + private final Pallet[] pallets; + + public PalletCollection(Pallet @NonNull ... pallets) { + Preconditions.checkArgument(pallets.length > 0); + Preconditions.checkArgument(pallets[0].getIndex() == 0); + Preconditions.checkArgument(pallets.length == 1 || + IntStream.range(1, pallets.length) + .allMatch(i -> pallets[i].getIndex() == pallets[i - 1].getIndex() + 1)); + + this.pallets = pallets; + } + + public Pallet get(int index) { + return pallets[index]; + } + + public int size() { + return pallets.length; + } +} diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java index ec2674eb..1f0fb00d 100644 --- a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java @@ -3,12 +3,12 @@ import com.strategyobject.substrateclient.common.reflection.ClassUtils; import com.strategyobject.substrateclient.common.reflection.Scanner; import com.strategyobject.substrateclient.common.types.Array; -import com.strategyobject.substrateclient.common.types.AutoRegistry; import com.strategyobject.substrateclient.common.types.Unit; import com.strategyobject.substrateclient.rpc.RpcDecoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContextFactory; import com.strategyobject.substrateclient.rpc.decoders.*; -import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import lombok.val; @@ -19,14 +19,10 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j -public class RpcDecoderRegistry implements AutoRegistry { - private final ScaleReaderRegistry scaleReaderRegistry; - +public class RpcDecoderRegistry { private final Map, RpcDecoder> decoders; - public RpcDecoderRegistry(ScaleReaderRegistry scaleReaderRegistry) { - this.scaleReaderRegistry = scaleReaderRegistry; - + public RpcDecoderRegistry() { decoders = new ConcurrentHashMap<>(128); register(new ListDecoder(), List.class); register(new MapDecoder(), Map.class); @@ -43,7 +39,7 @@ public RpcDecoderRegistry(ScaleReaderRegistry scaleReaderRegistry) { register(new ArrayDecoder(), Array.class); } - public void registerAnnotatedFrom(String... prefixes) { + public void registerAnnotatedFrom(RpcDecoderContextFactory rpcDecoderContextFactory, String... prefixes) { Scanner.forPrefixes(prefixes) .getSubTypesOf(RpcDecoder.class).forEach(decoder -> { val autoRegister = decoder.getAnnotation(AutoRegister.class); @@ -59,8 +55,8 @@ public void registerAnnotatedFrom(String... prefixes) { if (ClassUtils.hasDefaultConstructor(decoder)) { rpcDecoder = decoder.newInstance(); } else { - val ctor = decoder.getConstructor(RpcDecoderRegistry.class, ScaleReaderRegistry.class); - rpcDecoder = ctor.newInstance(this, scaleReaderRegistry); + val ctor = decoder.getConstructor(RpcDecoderContext.class); + rpcDecoder = ctor.newInstance(rpcDecoderContextFactory.create()); } register(rpcDecoder, types); diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcEncoderRegistry.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcEncoderRegistry.java index b70701f1..932e22fe 100644 --- a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcEncoderRegistry.java +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcEncoderRegistry.java @@ -3,13 +3,13 @@ import com.strategyobject.substrateclient.common.reflection.ClassUtils; import com.strategyobject.substrateclient.common.reflection.Scanner; import com.strategyobject.substrateclient.common.types.Array; -import com.strategyobject.substrateclient.common.types.AutoRegistry; import com.strategyobject.substrateclient.common.types.Unit; import com.strategyobject.substrateclient.rpc.RpcDispatch; import com.strategyobject.substrateclient.rpc.RpcEncoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContextFactory; import com.strategyobject.substrateclient.rpc.encoders.*; -import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import lombok.val; @@ -20,14 +20,10 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j -public class RpcEncoderRegistry implements AutoRegistry { - private final ScaleWriterRegistry scaleWriterRegistry; - +public class RpcEncoderRegistry { private final Map, RpcEncoder> encoders; - public RpcEncoderRegistry(@NonNull ScaleWriterRegistry scaleWriterRegistry) { - this.scaleWriterRegistry = scaleWriterRegistry; - + public RpcEncoderRegistry() { encoders = new ConcurrentHashMap<>(128); register(new PlainEncoder<>(), Void.class, void.class, String.class, Boolean.class, boolean.class, Byte.class, byte.class, @@ -40,7 +36,7 @@ public RpcEncoderRegistry(@NonNull ScaleWriterRegistry scaleWriterRegistry) { register(new DispatchingEncoder<>(this), RpcDispatch.class); } - public void registerAnnotatedFrom(String... prefixes) { + public void registerAnnotatedFrom(RpcEncoderContextFactory rpcEncoderContextFactory, String... prefixes) { Scanner.forPrefixes(prefixes) .getSubTypesOf(RpcEncoder.class).forEach(encoder -> { val autoRegister = encoder.getAnnotation(AutoRegister.class); @@ -56,8 +52,8 @@ public void registerAnnotatedFrom(String... prefixes) { if (ClassUtils.hasDefaultConstructor(encoder)) { rpcEncoder = encoder.newInstance(); } else { - val ctor = encoder.getConstructor(RpcEncoderRegistry.class, ScaleWriterRegistry.class); - rpcEncoder = ctor.newInstance(this, scaleWriterRegistry); + val ctor = encoder.getConstructor(RpcEncoderContext.class); + rpcEncoder = ctor.newInstance(rpcEncoderContextFactory.create()); } register(rpcEncoder, types); diff --git a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/encoders/DispatchingEncoderTest.java b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/encoders/DispatchingEncoderTest.java index 4b319a7c..5e443b36 100644 --- a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/encoders/DispatchingEncoderTest.java +++ b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/encoders/DispatchingEncoderTest.java @@ -5,14 +5,13 @@ import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; import com.strategyobject.substrateclient.rpc.substitutes.TestDispatch; import com.strategyobject.substrateclient.rpc.substitutes.TestDispatchEncoder; -import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; import lombok.val; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class DispatchingEncoderTest { - private final RpcEncoderRegistry registry = new RpcEncoderRegistry(new ScaleWriterRegistry()) {{ + private final RpcEncoderRegistry registry = new RpcEncoderRegistry() {{ register(new TestDispatchEncoder(this), TestDispatch.class); }}; diff --git a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollectionTest.java b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollectionTest.java new file mode 100644 index 00000000..9b19dba2 --- /dev/null +++ b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/metadata/PalletCollectionTest.java @@ -0,0 +1,44 @@ +package com.strategyobject.substrateclient.rpc.metadata; + +import lombok.val; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class PalletCollectionTest { + @Test + void get() { + val pallet1 = mock(Pallet.class); + when(pallet1.getIndex()).thenReturn(0); + val pallet2 = mock(Pallet.class); + when(pallet2.getIndex()).thenReturn(1); + val pallet3 = mock(Pallet.class); + when(pallet3.getIndex()).thenReturn(2); + + PalletCollection palletCollection = new PalletCollection( + pallet1, + pallet2, + pallet3 + ); + + assertEquals(3, palletCollection.size()); + assertEquals(pallet1, palletCollection.get(0)); + assertEquals(pallet2, palletCollection.get(1)); + assertEquals(pallet3, palletCollection.get(2)); + } + + @Test + void failsWhenIndexIsNotIncremental() { + val pallet1 = mock(Pallet.class); + when(pallet1.getIndex()).thenReturn(0); + val pallet2 = mock(Pallet.class); + when(pallet2.getIndex()).thenReturn(1); + val pallet3 = mock(Pallet.class); + when(pallet3.getIndex()).thenReturn(1); + + assertThrows(IllegalArgumentException.class, () -> new PalletCollection(pallet1, pallet2, pallet3)); + } +} \ No newline at end of file diff --git a/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleReaderRegistry.java b/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleReaderRegistry.java index 33e3cab6..51a4ee67 100644 --- a/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleReaderRegistry.java +++ b/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleReaderRegistry.java @@ -3,7 +3,6 @@ import com.strategyobject.substrateclient.common.reflection.ClassUtils; import com.strategyobject.substrateclient.common.reflection.Scanner; import com.strategyobject.substrateclient.common.types.Array; -import com.strategyobject.substrateclient.common.types.AutoRegistry; import com.strategyobject.substrateclient.common.types.Result; import com.strategyobject.substrateclient.common.types.Unit; import com.strategyobject.substrateclient.common.types.union.*; @@ -24,7 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j -public class ScaleReaderRegistry implements AutoRegistry { +public class ScaleReaderRegistry { private final Map, ScaleReader> readers; public ScaleReaderRegistry() { diff --git a/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleWriterRegistry.java b/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleWriterRegistry.java index 91c9e72f..d7fa4eb0 100644 --- a/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleWriterRegistry.java +++ b/scale/src/main/java/com/strategyobject/substrateclient/scale/registries/ScaleWriterRegistry.java @@ -3,7 +3,6 @@ import com.strategyobject.substrateclient.common.reflection.ClassUtils; import com.strategyobject.substrateclient.common.reflection.Scanner; import com.strategyobject.substrateclient.common.types.Array; -import com.strategyobject.substrateclient.common.types.AutoRegistry; import com.strategyobject.substrateclient.common.types.Result; import com.strategyobject.substrateclient.common.types.Unit; import com.strategyobject.substrateclient.common.types.union.*; @@ -25,7 +24,7 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j -public class ScaleWriterRegistry implements AutoRegistry { +public class ScaleWriterRegistry { private final Map, ScaleWriter> writers; public ScaleWriterRegistry() {