From c9d7943a87b92e11b1e71bf0752d1d725240a979 Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Sun, 28 Jun 2020 23:27:13 -0400 Subject: [PATCH] solution: implement missing rpc shortcuts to be on par with JS reference lib --- docs/ref-01-api-commands.adoc | 44 +++++- .../polkaj/api/StandardCommands.java | 129 +++++++++++++++++- .../polkaj/api/StandardSubscriptions.java | 17 ++- .../api/StandardSubscriptionsSpec.groovy | 42 +++++- .../emeraldpay/polkaj/json/ReadProofJson.java | 42 ++++++ .../polkaj/json/StorageChangeSetJson.java | 79 +++++++++++ .../polkaj/json/ReadProofJsonSpec.groovy | 17 +++ .../json/StorageChangeSetJsonSpec.groovy | 26 ++++ 8 files changed, 384 insertions(+), 12 deletions(-) create mode 100644 polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/ReadProofJson.java create mode 100644 polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/StorageChangeSetJson.java create mode 100644 polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/ReadProofJsonSpec.groovy create mode 100644 polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/StorageChangeSetJsonSpec.groovy diff --git a/docs/ref-01-api-commands.adoc b/docs/ref-01-api-commands.adoc index 0c65718..0a4f6f7 100644 --- a/docs/ref-01-api-commands.adoc +++ b/docs/ref-01-api-commands.adoc @@ -1,5 +1,9 @@ = Standard RPC Commands and Subscriptions +- <> +- <> + +[#calls] == RPC Commands :shortcut-base: StandardCommands.getInstance() @@ -16,6 +20,7 @@ * State - <> - <> +- <> * System - <> - <> @@ -106,7 +111,10 @@ Get value under a specified storage key in a contract. Shortcut:: `{shortcut-base}.contractsGetStorage(address, key, at)` Command:: `contracts_getStorage` -Parameters:: `address` - contract address (`Address`), `key` - key (`Has256`), `at` - (optional) block reference (`Hash256`) +Parameters:: + - `address` - contract address (`Address`), + - `key` - key (`Has256`), + - `at` - (optional) block reference (`Hash256`) Java Object:: `ByteData` [#contractsRentProjection] @@ -116,7 +124,9 @@ Get projected time a given contract will be able to sustain paying its rent Shortcut:: `{shortcut-base}.contractsRentProjection(address, at)` Command:: `contracts_getStorage` -Parameters:: `address` - contract address (`Address`), `at` - (optional) block reference (`Hash256`) +Parameters:: + - `address` - contract address (`Address`), + - `at` - (optional) block reference (`Hash256`) Java Object:: `Long` @@ -148,6 +158,18 @@ Command:: `state_getStorage` Parameters:: `key` - bytes (`byte[]` or `ByteDate`) Java Object:: `ByteData` +[#stateGetReadProof] +=== State Get Read Proof (`state_getReadProof`) + +Get proof of storage entries at a specific block state + +Shortcut:: `{shortcut-base}.stateGetReadProof(keys, at?)` +Command:: `state_getReadProof` +Parameters:: + - `keys` - list of keys (`List`) + - `at` - (optional) block reference (`Hash256`) +Java Object:: `ReadProofJson` + [#systemChain] === System Chain (`system_chain`) @@ -234,12 +256,14 @@ Java Object:: `MethodsJson` - `Integer getVersion()` - version of RPC - `List getMethods()` - list of methods +[#subsriptions] == Subscriptions :shortcut-base: StandardSubscriptions.getInstance() - <> - <> - <> +- <> [#subFinalizedHeads] === Finalized Heads (`chain_subscribeFinalizedHeads`) @@ -262,11 +286,21 @@ Parameters:: none Java Object:: `BlockJson.Header` [#subRuntimeVersion] -=== Runtime Version (`chain_subscribeRuntimeVersion`) +=== Runtime Version (`state_subscribeRuntimeVersion`) Subscribe to the changes to the Runtime Version. Shortcut:: `{shortcut-base}.runtimeVersion()` -Command:: `chain_subscribeRuntimeVersion` +Command:: `state_subscribeRuntimeVersion` Parameters:: none -Java Object:: `RuntimeVersion` \ No newline at end of file +Java Object:: `RuntimeVersion` + +[#subStorage] +=== Storage (`state_subscribeStorage`) + +Subscribe to the changes to the Storage. + +Shortcut:: `{shortcut-base}.storage(keys?)` +Command:: `state_subscribeRuntimeVersion` +Parameters:: `keys` - (optional) Storage Keys (`Hash256`) +Java Object:: `StorageChangeSetJson` \ No newline at end of file diff --git a/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardCommands.java b/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardCommands.java index b8c8698..082bb66 100644 --- a/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardCommands.java +++ b/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardCommands.java @@ -5,6 +5,7 @@ import io.emeraldpay.polkaj.types.ByteData; import io.emeraldpay.polkaj.types.Hash256; +import java.util.ArrayList; import java.util.List; /** @@ -125,11 +126,133 @@ public RpcCall stateMetadata() { /** * Request data from storage - * @param request key (depending on the storage) + * @param key key (depending on the storage) * @return command */ - public RpcCall stateGetStorage(ByteData request) { - return RpcCall.create(ByteData.class, "state_getStorage", request.toString()); + public RpcCall stateGetStorage(ByteData key) { + return RpcCall.create(ByteData.class, "state_getStorage", key.toString()); + } + + /** + * @deprecated Use RpcCall.create(Hash256.class, "state_getStorageHash", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateGetStorageHash(ByteData key, Hash256 at) { + List params = at == null ? List.of(key) : List.of(key, at); + return RpcCall.create(Hash256.class, "state_getStorageHash", params); + } + + /** + * @deprecated Use RpcCall.create(Hash256.class, "state_getStorageSize", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateGetStorageSize(ByteData key, Hash256 at) { + List params = at == null ? List.of(key) : List.of(key, at); + return RpcCall.create(Long.class, "state_getStorageSize", params); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_call", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateCall(String method, ByteData data, Hash256 at) { + List params = at == null ? List.of(method, data) : List.of(method, data, at); + return RpcCall.create(ByteData.class, "state_call", params); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_getChildKeys", ...).expectList() + */ + @Deprecated(forRemoval = true) + public RpcCall> stateGetChildKeys(ByteData childStorageKey, ByteData childDefinition, long childType, ByteData key, Hash256 at) { + List params = new ArrayList<>(List.of(childStorageKey, childDefinition, childType, key)); + if (at != null) { + params.add(at); + } + return RpcCall.create(ByteData.class, "state_getChildKeys", params).expectList(); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_getChildStorage", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateGetStorageData(ByteData childStorageKey, ByteData childDefinition, long childType, ByteData key, Hash256 at) { + List params = new ArrayList<>(List.of(childStorageKey, childDefinition, childType, key)); + if (at != null) { + params.add(at); + } + return RpcCall.create(ByteData.class, "state_getChildStorage", params); + } + + /** + * @deprecated Use RpcCall.create(Hash256.class, "state_getChildStorageHash", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateGetChildStorageHash(ByteData childStorageKey, ByteData childDefinition, long childType, ByteData key, Hash256 at) { + List params = new ArrayList<>(List.of(childStorageKey, childDefinition, childType, key)); + if (at != null) { + params.add(at); + } + return RpcCall.create(Hash256.class, "state_getChildStorageHash", params); + } + + /** + * @deprecated RpcCall.create(Long.class, "state_getChildStorageSize", ...) + */ + @Deprecated(forRemoval = true) + public RpcCall stateGetChildStorageSize(ByteData childStorageKey, ByteData childDefinition, long childType, ByteData key, Hash256 at) { + List params = new ArrayList<>(List.of(childStorageKey, childDefinition, childType, key)); + if (at != null) { + params.add(at); + } + return RpcCall.create(Long.class, "state_getChildStorageSize", params); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_getKeysPaged", ...).expectList() + */ + @Deprecated(forRemoval = true) + public RpcCall> stateGetKeys(ByteData key, int count, ByteData startKey, Hash256 at) { + List params = new ArrayList<>(List.of(key, count)); + if (startKey != null) { + params.add(startKey); + } + if (at != null) { + params.add(at); + } + return RpcCall.create(ByteData.class, "state_getKeysPaged", params).expectList(); + } + + public RpcCall stateGetReadProof(List keys, Hash256 at) { + List params = new ArrayList<>(List.of(keys)); + if (at != null) { + params.add(at); + } + return RpcCall.create(ReadProofJson.class, "state_getReadProof", params); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_queryStorage", ...).expectList() + */ + @Deprecated(forRemoval = true) + public RpcCall> stateQueryStorage(List keys, Hash256 fromBlock, Hash256 toBlock) { + List params = new ArrayList<>(List.of(keys, fromBlock)); + if (toBlock != null) { + params.add(toBlock); + } + return RpcCall.create(ByteData.class, "state_queryStorage", params).expectList(); + } + + /** + * @deprecated Use RpcCall.create(ByteData.class, "state_queryStorageAt", ...).expectList() + */ + @Deprecated(forRemoval = true) + public RpcCall> stateQueryStorageAt(List keys, Hash256 at) { + List params = new ArrayList<>(List.of(keys)); + if (at != null) { + params.add(at); + } + return RpcCall.create(ByteData.class, "state_queryStorageAt", params).expectList(); } /** diff --git a/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardSubscriptions.java b/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardSubscriptions.java index 42c42ef..bdeeea8 100644 --- a/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardSubscriptions.java +++ b/polkaj-api-base/src/main/java/io/emeraldpay/polkaj/api/StandardSubscriptions.java @@ -2,6 +2,10 @@ import io.emeraldpay.polkaj.json.BlockJson; import io.emeraldpay.polkaj.json.RuntimeVersionJson; +import io.emeraldpay.polkaj.json.StorageChangeSetJson; +import io.emeraldpay.polkaj.types.ByteData; + +import java.util.List; /** * Standard/common Polkadot subscriptions @@ -38,6 +42,17 @@ public SubscribeCall finalizedHeads() { * @return command */ public SubscribeCall runtimeVersion() { - return SubscribeCall.create(RuntimeVersionJson.class, "chain_subscribeRuntimeVersion", "chain_unsubscribeRuntimeVersion"); + return SubscribeCall.create(RuntimeVersionJson.class, "state_subscribeRuntimeVersion", "state_unsubscribeRuntimeVersion"); + } + + public SubscribeCall storage() { + return SubscribeCall.create(StorageChangeSetJson.class, "state_subscribeStorage", "state_unsubscribeStorage"); + } + + public SubscribeCall storage(List keys) { + if (keys == null || keys.isEmpty()) { + return storage(); + } + return SubscribeCall.create(StorageChangeSetJson.class, "state_subscribeStorage", "state_unsubscribeStorage", List.of(keys)); } } diff --git a/polkaj-api-base/src/test/groovy/io/emeraldpay/polkaj/api/StandardSubscriptionsSpec.groovy b/polkaj-api-base/src/test/groovy/io/emeraldpay/polkaj/api/StandardSubscriptionsSpec.groovy index e795d6b..a16d1cc 100644 --- a/polkaj-api-base/src/test/groovy/io/emeraldpay/polkaj/api/StandardSubscriptionsSpec.groovy +++ b/polkaj-api-base/src/test/groovy/io/emeraldpay/polkaj/api/StandardSubscriptionsSpec.groovy @@ -4,7 +4,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.type.TypeFactory import io.emeraldpay.polkaj.json.RuntimeVersionJson import io.emeraldpay.polkaj.json.BlockJson +import io.emeraldpay.polkaj.json.StorageChangeSetJson import io.emeraldpay.polkaj.json.jackson.PolkadotModule +import io.emeraldpay.polkaj.types.ByteData import spock.lang.Specification class StandardSubscriptionsSpec extends Specification { @@ -34,13 +36,47 @@ class StandardSubscriptionsSpec extends Specification { act.getResultType(typeFactory).getRawClass() == BlockJson.Header.class } - def "chain subscribe runtime vesion"() { + def "state subscribe runtime version"() { when: def act = StandardSubscriptions.getInstance().runtimeVersion() then: - act.method == "chain_subscribeRuntimeVersion" + act.method == "state_subscribeRuntimeVersion" act.params.size() == 0 - act.unsubscribe == "chain_unsubscribeRuntimeVersion" + act.unsubscribe == "state_unsubscribeRuntimeVersion" act.getResultType(typeFactory).getRawClass() == RuntimeVersionJson.class } + + def "state subscribe storage"() { + when: + def act = StandardSubscriptions.getInstance().storage() + then: + act.method == "state_subscribeStorage" + act.params.size() == 0 + act.unsubscribe == "state_unsubscribeStorage" + act.getResultType(typeFactory).getRawClass() == StorageChangeSetJson.class + + when: + act = StandardSubscriptions.getInstance().storage(null) + then: + act.method == "state_subscribeStorage" + act.params.size() == 0 + act.unsubscribe == "state_unsubscribeStorage" + act.getResultType(typeFactory).getRawClass() == StorageChangeSetJson.class + + when: + act = StandardSubscriptions.getInstance().storage([]) + then: + act.method == "state_subscribeStorage" + act.params.size() == 0 + act.unsubscribe == "state_unsubscribeStorage" + act.getResultType(typeFactory).getRawClass() == StorageChangeSetJson.class + + when: + act = StandardSubscriptions.getInstance().storage([ByteData.from("0x00")]) + then: + act.method == "state_subscribeStorage" + act.params.toList() == [[ByteData.from("0x00")]] + act.unsubscribe == "state_unsubscribeStorage" + act.getResultType(typeFactory).getRawClass() == StorageChangeSetJson.class + } } diff --git a/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/ReadProofJson.java b/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/ReadProofJson.java new file mode 100644 index 0000000..72b8066 --- /dev/null +++ b/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/ReadProofJson.java @@ -0,0 +1,42 @@ +package io.emeraldpay.polkaj.json; + +import io.emeraldpay.polkaj.types.ByteData; +import io.emeraldpay.polkaj.types.Hash256; + +import java.util.List; +import java.util.Objects; + +public class ReadProofJson { + private Hash256 at; + private List proof; + + public Hash256 getAt() { + return at; + } + + public void setAt(Hash256 at) { + this.at = at; + } + + public List getProof() { + return proof; + } + + public void setProof(List proof) { + this.proof = proof; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ReadProofJson)) return false; + ReadProofJson that = (ReadProofJson) o; + return Objects.equals(at, that.at) && + Objects.equals(proof, that.proof); + } + + @Override + public int hashCode() { + return Objects.hash(at, proof); + } +} diff --git a/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/StorageChangeSetJson.java b/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/StorageChangeSetJson.java new file mode 100644 index 0000000..c03640c --- /dev/null +++ b/polkaj-json-types/src/main/java/io/emeraldpay/polkaj/json/StorageChangeSetJson.java @@ -0,0 +1,79 @@ +package io.emeraldpay.polkaj.json; + +import io.emeraldpay.polkaj.types.ByteData; +import io.emeraldpay.polkaj.types.Hash256; + +import java.util.List; +import java.util.Objects; + +public class StorageChangeSetJson { + + private Hash256 block; + private List changes; + + public Hash256 getBlock() { + return block; + } + + public void setBlock(Hash256 block) { + this.block = block; + } + + public List getChanges() { + return changes; + } + + public void setChanges(List changes) { + this.changes = changes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StorageChangeSetJson)) return false; + StorageChangeSetJson that = (StorageChangeSetJson) o; + return Objects.equals(block, that.block) && + Objects.equals(changes, that.changes); + } + + @Override + public int hashCode() { + return Objects.hash(block, changes); + } + + public static class KeyValueOption { + private Hash256 key; + private ByteData data; + + public Hash256 getKey() { + return key; + } + + public void setKey(Hash256 key) { + this.key = key; + } + + public ByteData getData() { + return data; + } + + public void setData(ByteData data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof KeyValueOption)) return false; + KeyValueOption that = (KeyValueOption) o; + return Objects.equals(key, that.key) && + Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(key, data); + } + } + +} diff --git a/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/ReadProofJsonSpec.groovy b/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/ReadProofJsonSpec.groovy new file mode 100644 index 0000000..f2d345e --- /dev/null +++ b/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/ReadProofJsonSpec.groovy @@ -0,0 +1,17 @@ +package io.emeraldpay.polkaj.json + +import nl.jqno.equalsverifier.EqualsVerifier +import nl.jqno.equalsverifier.Warning +import spock.lang.Specification + +class ReadProofJsonSpec extends Specification { + + def "Equals and HashCode work"() { + when: + def v = EqualsVerifier.forClass(ReadProofJson) + .suppress(Warning.STRICT_INHERITANCE) + .suppress(Warning.NONFINAL_FIELDS) + then: + v.verify() + } +} diff --git a/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/StorageChangeSetJsonSpec.groovy b/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/StorageChangeSetJsonSpec.groovy new file mode 100644 index 0000000..7f09ac8 --- /dev/null +++ b/polkaj-json-types/src/test/groovy/io/emeraldpay/polkaj/json/StorageChangeSetJsonSpec.groovy @@ -0,0 +1,26 @@ +package io.emeraldpay.polkaj.json + +import nl.jqno.equalsverifier.EqualsVerifier +import nl.jqno.equalsverifier.Warning +import spock.lang.Specification + +class StorageChangeSetJsonSpec extends Specification { + + def "Equals and HashCode work"() { + when: + def v = EqualsVerifier.forClass(StorageChangeSetJson) + .suppress(Warning.STRICT_INHERITANCE) + .suppress(Warning.NONFINAL_FIELDS) + then: + v.verify() + } + + def "Equals and HashCode work for KeyValueOption"() { + when: + def v = EqualsVerifier.forClass(StorageChangeSetJson.KeyValueOption) + .suppress(Warning.STRICT_INHERITANCE) + .suppress(Warning.NONFINAL_FIELDS) + then: + v.verify() + } +}