Skip to content

Commit 5b6cdbd

Browse files
committed
AUTO: Sync ScalarDL docs in English to docs site repo
1 parent 38dcd3e commit 5b6cdbd

File tree

97 files changed

+9289
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+9289
-0
lines changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
---
2+
tags:
3+
- Community
4+
- Enterprise
5+
displayed_sidebar: docsEnglish
6+
---
7+
8+
# A simple bank account application
9+
10+
## Overview
11+
12+
This is a simple bank account application, which can be found in the [`scalardl` repository](https://github.com/scalar-labs/scalardl/tree/master/docs/applications/simple-bank-account/). The actions that a user can perform are: create an account, view an account history, deposit funds to an account, withdraw funds from an account, and transfer funds between accounts. All actions performed on an account are recorded in ScalarDL, which means that the account history is recorded in a tamper-evident way, similar to how blockchains record blocks. This means that if an account history was altered (either intentionally or not), it is possible to detect this.
13+
14+
To keep things simple here we are assuming that the bank holds the private key to execute all the contracts (see below for more explanation of how this works). This is probably not how you would want to use this bank application in practice. In this case a malicious account manager could actually change a user's account history, e.g., by simply recreating it and filling it with false data. A more meaningful setup is that the bank owns the private key to deposit to an account, and each user registers a withdrawal and transfer contract using their own private key. Then only the bank can move funds into an account, and only users can move funds out of their accounts.
15+
16+
This application uses five contracts:
17+
18+
- [`AccountHistory.java`](https://github.com/scalar-labs/scalardl/blob/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract/AccountHistory.java)
19+
- [`CreateAccount.java`](https://github.com/scalar-labs/scalardl/blob/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract/CreateAccount.java)
20+
- [`Deposit.java`](https://github.com/scalar-labs/scalardl/blob/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract/Deposit.java)
21+
- [`Transfer.java`](https://github.com/scalar-labs/scalardl/blob/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract/Transfer.java)
22+
- [`Withdraw.java`](https://github.com/scalar-labs/scalardl/blob/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract/Withdraw.java)
23+
24+
These contracts will be registered by the bank and will allow the bank to, respectively, view account histories, create accounts, deposit funds to an account, transfer funds between accounts, and withdraw funds from accounts.
25+
26+
The overall architecture of this application can be viewed as follows. (Note again that this use case is for simplicity, and in practice may look a bit different.)
27+
28+
![architecture](./docs/img/architecture.jpg)
29+
30+
## Prerequisites for this sample application
31+
32+
- OpenJDK LTS version (8, 11, 17, or 21) from [Eclipse Temurin](https://adoptium.net/temurin/releases/)
33+
34+
:::note
35+
36+
- Using an LTS version mentioned above is recommended, but other non-LTS versions may work.
37+
- Other JDKs should work with this sample application, but they haven't been tested.
38+
39+
:::
40+
41+
## Trying out the application
42+
43+
Download the [ScalarDL Client SDK](https://github.com/scalar-labs/scalardl-client-sdk). Make sure ScalarDL is running and register all the required contracts by executing
44+
45+
```console
46+
./gradlew build
47+
cd contract
48+
SCALAR_SDK_HOME=/path/to/scalardl-client-sdk ./register
49+
```
50+
Run the application using IntelliJ (or the IDE of your choice), or by executing `gradle bootRun` in the project home directory. It should create a server on `localhost:8080` to which you can send HTTP requests in order to interact with the app. See the [API documentation](./docs/api_endpoints.mdx) for more information. To create HTTP requests we have found that [Postman](https://www.getpostman.com/) is quite nice.
51+
52+
## A short tutorial on writing a ScalarDL application
53+
54+
We decided to use Spring Boot to create a web service to interact with the contracts. This is, of course, not the only choice. Another choice would be to create a command line interface as was done, for example, in the [asset management application](https://github.com/indetail-blockchain/getting-started-with-scalardl). There you can also find a very nice tutorial for writing applications for ScalarDL.
55+
56+
In this tutorial we will not discuss the detail at the level of web services or command line interfaces, and instead focus on the interaction between our application and ScalarDL. We will discuss how to write contracts, register contracts, and then how to call these contracts from the application using the ScalarDL SDK.
57+
58+
### Contracts
59+
60+
Contracts are Java classes which extend the `JacksonBasedContract` class and override the `invoke` method. Let's take a closer look at the `Deposit.java` contract.
61+
62+
```java
63+
package com.scalar.application.bankaccount.contract;
64+
65+
import com.fasterxml.jackson.databind.JsonNode;
66+
import com.scalar.dl.ledger.statemachine.Asset;
67+
import com.scalar.dl.ledger.contract.JacksonBasedContract;
68+
import com.scalar.dl.ledger.exception.ContractContextException;
69+
import com.scalar.dl.ledger.statemachine.Ledger;
70+
import java.util.Optional;
71+
import javax.annotation.Nullable;
72+
73+
public class Deposit extends JacksonBasedContract {
74+
@Override
75+
public JsonNode invoke(
76+
Ledger<JsonNode> ledger, JsonNode argument, @Nullable JsonNode properties) {
77+
if (!argument.has("id") || !argument.has("amount")) {
78+
throw new ContractContextException("a required key is missing: id and/or amount");
79+
}
80+
81+
String id = argument.get("id").asText();
82+
long amount = argument.get("amount").asLong();
83+
84+
if (amount < 0) {
85+
throw new ContractContextException("amount is negative");
86+
}
87+
88+
Optional<Asset<JsonNode>> asset = ledger.get(id);
89+
90+
if (!asset.isPresent()) {
91+
throw new ContractContextException("account does not exist");
92+
}
93+
94+
long oldBalance = asset.get().data().get("balance").asLong();
95+
long newBalance = oldBalance + amount;
96+
97+
ledger.put(id, getObjectMapper().createObjectNode().put("balance", newBalance));
98+
return getObjectMapper()
99+
.createObjectNode()
100+
.put("status", "succeeded")
101+
.put("old_balance", oldBalance)
102+
.put("new_balance", newBalance);
103+
}
104+
}
105+
```
106+
107+
In order for this contract to function properly the user must supply an account `id` and an `amount`. So the first thing to do is check whether the argument contains these two keys, and if not, throw a `ContractContextException`.
108+
109+
**Note:** `ContractContextException` is the only throwable exception in a contract and it should be thrown whenever a non-recoverable error is encountered.
110+
111+
So, assuming that we have an `id` and an `amount`, we do a quick non-negative check on `amount` and again throw a `ContractContextException` if it is. Now we are ready to interact with the `ledger`.
112+
113+
There are three methods that can be called on `ledger`: `get(String s)`, `put(String s, JsonNode jsonNode)`, and `scan(AssetFilter assetFilter)`. `get(String s)` will retrieve the asset `s` from the ledger. `put(String s, JsonNode jsonNode)` will associate the asset `s` with the data `jsonNode` and increase the age of the asset. `scan(AssetFilter assetFilter)` will return a version of the history of an asset as specified in the `AssetFilter`.
114+
115+
**Note:** ledger does not permit blind writes, i.e., before performing a `put` on a particular asset, we must first `get` that asset. Furthermore `scan` is only allowed in read-only contracts, which means a single contract cannot both `scan` and `put`.
116+
117+
The rest of the contract proceeds in a straightforward manner. We first `get` the asset from the ledger, retrieve its current balance, add the deposit amount to it, and finally `put` the asset back into the ledger with its new balance.
118+
119+
At the end we must return a `JsonNode`. What the `JsonNode` contains is up to the designer of the contract. Here we have decided to include a `status` message, the `old_balance`, and the `new_balance`.
120+
121+
If you wish, you can view the other contracts that this application uses in the [`contract` folder for this sample on GitHub](https://github.com/scalar-labs/scalardl/tree/master/docs/applications/simple-bank-account/contract/src/main/java/com/scalar/application/bankaccount/contract).
122+
123+
Once you have written your contracts you will need to compile them, and this can be done as
124+
125+
```console
126+
./gradlew build
127+
```
128+
129+
### Registering your certification and contracts
130+
131+
You should now have written and compiled your contracts. Before you can execute them, however, you will need to register them on the ScalarDL network. We will make use of the tools available in the [ScalarDL Client SDK](https://github.com/scalar-labs/scalardl-client-sdk) `client/bin` directory to register and execute the contracts. Please make sure you have access to this directory.
132+
133+
Now, you will need to have your certificate (e.g. `client.pem`) and its corresponding private key (e.g. `client-key.pem`), and ScalarDL up and running. Edit `client.properties` (found in the `conf` directory) to suit your configuration. It should contain lines that look something like:
134+
135+
```console
136+
scalar.dl.client.server.host=localhost
137+
scalar.dl.client.server.port=50051
138+
scalar.dl.client.cert_holder_id=alice
139+
scalar.dl.client.cert_path=conf/client.pem
140+
scalar.dl.client.private_key_path=conf/client-key.pem
141+
```
142+
143+
If everything is set up properly you should be able to register your certificate on the ScalarDL network as
144+
145+
```console
146+
cd contract
147+
${SCALAR_SDK_HOME}/client/bin/scalardl register-cert --properties ../conf/client.properties
148+
```
149+
150+
You should receive status code 200 if successful.
151+
152+
To register your contracts you can create a `contracts.toml` file in the `conf` directory using the following format:
153+
154+
```toml
155+
[[contracts]]
156+
contract-id = "create-account"
157+
contract-binary-name = "com.scalar.application.bankaccount.contract.CreateAccount"
158+
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/CreateAccount.class"
159+
160+
[[contracts]]
161+
contract-id = "deposit"
162+
contract-binary-name = "com.scalar.application.bankaccount.contract.Deposit"
163+
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/Deposit.class"
164+
165+
[[contracts]]
166+
contract-id = "transfer"
167+
contract-binary-name = "com.scalar.application.bankaccount.contract.Transfer"
168+
contract-class-file = "build/classes/java/main/com/scalar/application/bankaccount/contract/Transfer.class"
169+
```
170+
171+
In this example we will register three contracts: `CreateAccount.java`, `Deposit.java`, and `Transfer.java`. The `contract-binary-name` and `contract-class-file` are determined, but you are free to choose the `contract-id` as you wish. The `contract-id` is how you can refer to a specific contract using `ClientService`, as we will see below.
172+
173+
Once your toml file is written you can register all the specified contracts as
174+
175+
```console
176+
${SCALAR_SDK_HOME}/client/bin/scalardl register-contracts --properties ../conf/client.properties --contracts-file ../conf/contracts.toml
177+
```
178+
179+
Each successfully registered contract should return status code 200.
180+
181+
### Executing contracts
182+
183+
You can now execute any registered contracts if you would like. For example, use our register contracts to create a couple of accounts, deposit funds into one of the accounts, transfer some of these funds to the other account, and check the account history.
184+
185+
Create two accounts with ids `a111` and `b222`. (Contract ids can be any string.)
186+
187+
```console
188+
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id create-account --contract-argument '{"id": "a111"}'
189+
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id create-account --contract-argument '{"id": "b222"}'
190+
```
191+
192+
Now, deposit 100 into account `a111`:
193+
194+
```console
195+
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id deposit --contract-argument '{"id": "a111", "amount": 100}'
196+
```
197+
198+
Finally, transfer 25 from `a111` to `b222`:
199+
200+
```console
201+
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id transfer --contract-argument '{"from": "a111", "to": "b222", "amount": 100}'
202+
```
203+
204+
You can check the balance history of account `a111` as follows:
205+
206+
```console
207+
${SCALAR_SDK_HOME}/client/bin/scalardl execute-contract --properties ../conf/client.properties --contract-id account-history --contract-argument '{"id": "a111"}'
208+
```
209+
210+
You should see the following output:
211+
212+
```console
213+
Contract result:
214+
{
215+
"status" : "succeeded",
216+
"history" : [ {
217+
"id" : "a111",
218+
"age" : 2,
219+
"data" : {
220+
"balance" : 0
221+
}
222+
}, {
223+
"id" : "a111",
224+
"age" : 1,
225+
"data" : {
226+
"balance" : 100
227+
}
228+
}, {
229+
"id" : "a111",
230+
"age" : 0,
231+
"data" : {
232+
"balance" : 0
233+
}
234+
} ]
235+
}
236+
```
237+
238+
If you were running the application itself, you could execute these commands using the [API endpoints](./docs/api_endpoints.mdx).
239+
240+
## ClientService
241+
242+
You should now have your contracts registered on the ScalarDL network. In order to execute these contracts from an application we will make use of `ClientService` class from the [ScalarDL Client SDK](https://github.com/scalar-labs/scalardl-client-sdk).
243+
244+
The Client SDK is available on [Maven Central](https://search.maven.org/search?q=a:scalardl-client-sdk), and it can be installed in your application using Gradle by adding the following dependency to your `build.gradle`:
245+
246+
```groovy
247+
dependencies {
248+
compile group: 'com.scalar-labs', name: 'scalardl-java-client-sdk', version: '3.9.2'
249+
}
250+
```
251+
252+
The following snippet shows how you can instantiate a `ClientService` object, where `properties` should be the path to your `client.properties` file.
253+
254+
```java
255+
ClientServiceFactory factory = new ClientServiceFactory();
256+
ClientService service = factory.create(new ClientConfig(new File(properties)));
257+
```
258+
259+
`ClientService` contains a method `executeContract(String contractId, JsonNode contractArgument)` which can be used to, of course, execute a contract. For example:
260+
261+
```java
262+
ObjectMapper mapper = new ObjectMapper();
263+
JsonNode argument = mapper.createObjectNode().put("id", "010-123456789");
264+
ContractExecutionResult result = clientService.executeContract("create-account", argument);
265+
```
266+
267+
will execute the `CreateAccount` contract with argument `{"id": "010-123456789"}`, as we did above. Note that we call the contract using the supplied id `create-account` that we chose when registering the contract.
268+
269+
The result of executing the contract is a `ContractExecutionResult`. It contains, result and proofs, each of which can be obtained respectively as
270+
271+
```java
272+
result.getProofs();
273+
result.getResult();
274+
```
275+
276+
## What is next?
277+
278+
We hope that this has provided you with enough information to get started writing your own apps. Here are some ideas of what you can try next.
279+
280+
- Visit the [ScalarDL Client SDK](https://github.com/scalar-labs/scalardl-client-sdk) github page.
281+
- The [ScalarDL Emulator](https://github.com/scalar-labs/scalardl-emulator) lets you test your contracts on an in-memory ledger.
282+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
buildscript {
2+
repositories {
3+
mavenCentral()
4+
}
5+
dependencies {
6+
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
7+
}
8+
}
9+
10+
plugins {
11+
id 'java'
12+
id 'application'
13+
id 'idea'
14+
id "org.springframework.boot" version "2.1.1.RELEASE"
15+
id "io.spring.dependency-management" version "1.0.6.RELEASE"
16+
}
17+
18+
bootJar {
19+
baseName = 'gs-rest-service'
20+
version = '0.1.0'
21+
}
22+
23+
repositories {
24+
mavenCentral()
25+
}
26+
27+
sourceCompatibility = 1.8
28+
targetCompatibility = 1.8
29+
group = 'com.scalar.application.simple-bank-account'
30+
version = '0.1'
31+
32+
dependencies {
33+
compile('org.springframework.boot:spring-boot-starter-web') {
34+
exclude module: 'logback-classic'
35+
}
36+
compile group: 'com.scalar-labs', name: 'scalardl-java-client-sdk', version: '2.0.4'
37+
testCompile 'org.springframework.boot:spring-boot-starter-test'
38+
testCompile group: 'junit', name: 'junit', version: '4.12'
39+
testCompile 'org.assertj:assertj-core:3.9.1'
40+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIFwQjU2tmTbVWDEwcwJGT9IPrEE317pg2xpScqdcE/LSoAoGCCqGSM49
3+
AwEHoUQDQgAEvXTODt47J53wtR0XI0auVUD51gG7DlRHJ2ZAKeKlOi70F7FEKN84
4+
ag+BhP+KbzlSTpijDcprsQpky5GguTV07Q==
5+
-----END EC PRIVATE KEY-----
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICZDCCAgqgAwIBAgIUZflapxR4pNwA+ZDYSvNOmukkb/MwCgYIKoZIzj0EAwIw
3+
bzELMAkGA1UEBhMCSlAxDjAMBgNVBAgTBVRva3lvMQ4wDAYDVQQHEwVUb2t5bzEf
4+
MB0GA1UEChMWU2FtcGxlIEludGVybWVkaWF0ZSBDQTEfMB0GA1UEAxMWU2FtcGxl
5+
IEludGVybWVkaWF0ZSBDQTAeFw0xOTAxMjEwNjIzMDBaFw0yMjAxMjAwNjIzMDBa
6+
MB0xCzAJBgNVBAYTAmpwMQ4wDAYDVQQHEwV0b2t5bzBZMBMGByqGSM49AgEGCCqG
7+
SM49AwEHA0IABL10zg7eOyed8LUdFyNGrlVA+dYBuw5URydmQCnipTou9BexRCjf
8+
OGoPgYT/im85Uk6Yow3Ka7EKZMuRoLk1dO2jgdUwgdIwDgYDVR0PAQH/BAQDAgWg
9+
MBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBoa
10+
q5c0VYxPh0D23dRUOBf7mB/kMB8GA1UdIwQYMBaAFApqL9sG4IYXy3tSZ7cR/t9t
11+
ExUTMDEGCCsGAQUFBwEBBCUwIzAhBggrBgEFBQcwAYYVaHR0cDovL2xvY2FsaG9z
12+
dDo4ODg5MCoGA1UdHwQjMCEwH6AdoBuGGWh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC9j
13+
cmwwCgYIKoZIzj0EAwIDSAAwRQIhAIxFkRCPt17AO21xeV+4JMFnUg32wqBxs4Wa
14+
O26DtC1fAiB456Q+JJk+fpNIQ+nICAzKqUfoaQhzrgwQiBUpxiHfYQ==
15+
-----END CERTIFICATE-----
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Optional. A hostname or an IP address of the server. Use localhost by default if not specified.
2+
# It assuems that there is a single endpoint that is given by DNS or a load balancer.
3+
scalar.dl.client.server.host=localhost
4+
5+
# Optional. A port number of the server. Use 50051 by default if not specified.
6+
scalar.dl.client.server.port=50051
7+
8+
# Optional. A port number of the server for privileged services. Use 50052 by default if not specified.
9+
#scalar.dl.client.server.privileged_port=50052
10+
11+
# Required. The holder ID of a certificate.
12+
# It must be configured for each private key and unique in the system.
13+
scalar.dl.client.cert_holder_id=user1
14+
15+
# Optional. The version of the certificate. Use 1 by default if not specified.
16+
# Use another bigger integer if you need to change your private key.
17+
#scalar.dl.client.cert_version=1
18+
19+
# Required. The path of the certificate file.
20+
scalar.dl.client.cert_path=./conf/client.pem
21+
22+
# Required. The path of a corresponding private key file to the certificate.
23+
# Exceptionally it can be empty in some requests to privileged services
24+
# such as registerCertificate and registerFunction since they don't need a signature.
25+
scalar.dl.client.private_key_path=./conf/client-key.pem
26+
27+
# Optional. A flag to enable TLS communication. False by default.
28+
scalar.dl.client.tls.enabled=false
29+
30+
# Optional. A custom CA root certificate for TLS communication.
31+
# If the issuing certificate authority is known to the client, it can be empty.
32+
#scalar.dl.client.tls.ca_root_cert_path=/path/to/ca-root-cert
33+
34+
# Optional. An authorization credential. (e.g. authorization: Bearer token)
35+
# If this is given, clients will add "authorization: <credential>" http/2 header.
36+
#scalar.dl.client.authorization.credential=credential
37+
38+
# Experimental. Proxy server
39+
#scalar.dl.client.proxy.server=localhost:10051

0 commit comments

Comments
 (0)