Skip to content

Commit

Permalink
solution: docs for extrinsics
Browse files Browse the repository at this point in the history
  • Loading branch information
splix committed Aug 17, 2020
1 parent 4af86e1 commit b607b6a
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 184 deletions.
1 change: 1 addition & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ WARNING: UNDER DEVELOPMENT
- `io.emeraldpay.polkaj:polkaj-api-base:{lib-version}` - RPC base classes
- `io.emeraldpay.polkaj:polkaj-api-http:{lib-version}` - JSON RPC HTTP client
- `io.emeraldpay.polkaj:polkaj-api-ws:{lib-version}` - JSON RPC WebSocket client
- `io.emeraldpay.polkaj:polkaj-tx:{lib-version-dev}` - Storage access and Extrinsics

== Usage

Expand Down
182 changes: 0 additions & 182 deletions docs/05-balance.adoc

This file was deleted.

94 changes: 94 additions & 0 deletions docs/05-extrinsics.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
= Extrinsics and Storage

== How it works

With Polkadot you use Runtime to get value from storage or submit transactions (which are called _Extrinsics_).

To read data from storage, you prepare a query encoded as a hex string, and the result is usually a SCALE encoded object.
The encoded requests sent to `state_getStorage` (or related), and the response is a hex + SCALE encoded object.
The actual request parameters and the response type are described in the current Runtime Metadata object.

To send and execute an Extrinsic, is must be encoded as SCALE as well, signed, and submitted with `author_submitExtrinsic` (or related).

Polkaj is designed to be Runtime agnostic, and any of such requests could be build using Polkaj provided modules:

- `polkaj-scale`, `polkaj-scale-types`
- `polkaj-api-http` or `polkaj-api-ws`
- `polkaj-schnorrkel`
- `polkaj-tx`, with _Hashing_ and _ExtrinsicSigner_ classes specifically

== Storage Request

.`StorageRequest` interface
- provides `execute(PolkadotApi)` method to fetch the data
- or `ByteData encodeRequest()` method, as the RPC request query if you want to fetch manually
- methods `boolean isKeyEqualTo(ByteData key)` allows to verify the storage response, when used together with `state_subscribeStorage` RPC Subscription
- it `extends Function<ByteData, T>` to convert RPC response to a Java object, you may need it if you make a manual request

== Extrinsic Context

_Extrinsic_ details depend on the current Runtime state and other details of the current blockchain, such as height and genesis.
To create an Extrinsic you need to have all of them and use as a part of the encoded value and a signature.
It's all container by `ExtrinsicContext` class, which you have to prepare to sign an extrinsic.

There are two ways to prepare such context: manual and automatic from current RPC.

To build it manually, use `ExtrinsicContext.newBuilder()` and set all values (genesis, runtime version, and nonce).
The other way is to use `ExtrinsicContext.newAutoBuilder()` and fetch all those values from RPC.

.Manual context
[source, java]
----
// Current runtime version
RuntimeVersionJson runtimeVersion = client.execute(
StandardCommands.getInstance().getRuntimeVersion()
).get();
// Blockchain genesis block
Hash256 genesis = client.execute(
StandardCommands.getInstance().getBlockHash(0)
).get();
// Sender address info
AccountInfo accountInfo = AccountRequests.balanceOf(alice)
.execute(client)
.get();
// Build a context for the execution
ExtrinsicContext context = ExtrinsicContext.newAutoBuilder(alice)
// genesis block
.genesis(genesis)
// runtime version
.runtime(runtimeVersion)
// current sender nonce
.nonce(accountInfo.getNonce())
.build();
----

.Automatic context
[source, java]
----
ExtrinsicContext context = ExtrinsicContext.newAutoBuilder(alice, client)
// synchronious
.get()
.build();
----

Automatic context builder is easier to use, but with the manual builder you can save few RPC requests when you make multiple transfers.


== Extrinsic Signer

Class `ExtrinsicSigner` provides methdos for making a signature, or signature verification.
It's typed with a `Call` to sign, and supposed to be created with a SCALE writer for the call type it support.

.There're two main methods:
- `Hash512 sign(ExtrinsicContext ctx, CALL call, Schnorrkel.KeyPair key)` to sign the call under provided call context
- and `boolean isValid(ExtrinsicContext ctx, CALL call, Hash512 signature, Address address)` to verify an existing signature

== Extrinsic Request

Extrinsic Request class combines _Call Data_ and _Signature_ and prepares a payload to broadcast to the blockchain.

.`ExtrinsicRequest` interface
- provides `ByteData encodeRequest()` which encodes the transaction to send to RPC
108 changes: 108 additions & 0 deletions docs/06-balance.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
= Working with Account Balance

Polkaj provides Java-friendly wrappers to make some common operations.
`AccountRequests` class provides common operations for address and balance.

== Get total amount of issued coins

`totalIssuance()` method of `AccountRequests` class provides a functionality to request and process total amount of currently issues coins.
For the query it encodes request to storage function `TotalIssuance` of the module `Balances` on the current Runtime.

[source, java]
----
try (
PolkadotHttpApi client = PolkadotWsApi.newBuilder()
.connectTo("wss://cc3-5.kusama.network")
.build()
) {
DotAmount total = AccountRequests.totalIssuance()
// execute on RPC
.execute(client)
// get the value synchroniously
.get();
System.out.println(
"Total Issued: " + DotAmountFormatter.autoFormatter().format(total)
);
}
----

== Get balance

`balanceOf(address)` method of `AccountRequests` class provides a functionality to request and process account info for the specified address.
For the query it encodes request to storage function `System` of the module `Account` on the current Runtime.

[source, java]
----
try (
PolkadotHttpApi client = PolkadotWsApi.newBuilder()
.connectTo("wss://cc3-5.kusama.network")
.build()
) {
// request balance of Alice
Address alice = Address.from("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
// execute on RPC
AccountInfo accountInfo = AccountRequests.balanceOf(alice)
.execute(client)
.get();
System.out.println(
"Current balance: " +
DotAmountFormatter.autoFormatter().format(accountInfo.getData().getFree())
);
System.out.println(
"Current nonce : " +
accountInfo.getNonce()
);
}
----

== Transfer balance

`AccountRequests.transfer()` provides a _builder_ to prepare the extrinsic.
Use it to set values, such as _from_, _to_, and _amount_ to transfer.

To make a valid extrinsic, it also needs the current Runtime Metadata and Extrinsic Context with Key Pari to make a signature.

.Example
[source, java]
----
try (
PolkadotHttpApi client = PolkadotWsApi.newBuilder()
.connectTo("wss://cc3-5.kusama.network")
.build()
) {
// Build a context for the execution
ExtrinsicContext context = ExtrinsicContext.newAutoBuilder(alice, client)
.get()
.build();
// Current runtime meta
Metadata metadata = client.execute(
StandardCommands.getInstance().stateMetadata()
)
.thenApply(ScaleExtract.fromBytesData(new MetadataReader()))
.get();
// And build an actual call to the runtime method
AccountRequests.Transfer transfer = AccountRequests.transfer()
// get standard details from metadata (module and method id, etc)
.runtime(metadata)
// sender
.from(alice)
// recipient
.to(bob)
// amount to transfer
.amount(amount)
// sign with the context
.sign(aliceKey, context)
.build();
// Finally, submit to the blockchain
Hash256 txid = client.execute(
StandardCommands.getInstance()
.authorSubmitExtrinsic(transfer.encodeRequest())
).get();
}
----
Loading

0 comments on commit b607b6a

Please sign in to comment.