Ich habe versucht, den Java SDK-Beispielcode in der Hyperledger Iroha-Dokumentation auszuführen. Es hat lange gedauert, es zu bewegen, also werde ich verlassen, was ich getan habe.
Ich habe mich für das Verfahren auf dieses Dokument bezogen. https://iroha.readthedocs.io/ja/latest/getting_started/index.html
Ich habe ein Maven-Projekt in Eclipse erstellt und den Beispielcode ausgeführt. pom.xml Es war ein ziemlich Stolperstein vor dem Beispielcode, aber ich hatte große Probleme, ihn durch die Kompilierung zu bekommen. Als Ergebnis verschiedener Versuche wurde es die folgende pom.xml.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>iroha</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>mvn</id>
<url>https://mvnrepository.com</url>
</repository>
</repositories>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.22.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.grpc/grpc-netty -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.22.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.grpc/grpc-stub -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.22.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>protoc-gen-grpc-java</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>com.github.hyperledger</groupId>
<artifactId>iroha-java</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/jp.co.soramitsu/iroha -->
<dependency>
<groupId>jp.co.soramitsu</groupId>
<artifactId>iroha</artifactId>
<version>0.0.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.10.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.10.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testinfected.hamcrest-matchers/core-matchers -->
<dependency>
<groupId>org.testinfected.hamcrest-matchers</groupId>
<artifactId>core-matchers</artifactId>
<version>1.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArgument>-Werror</compilerArgument>
</configuration>
</plugin>
</plugins>
</build>
</project>
Es gibt zwei Punkte, die gestolpert sind.
Der erste Punkt scheint eine gewisse Abhängigkeit von der JAR-Datei von "protoc-gen-grpc-java" zu haben, und ein Fehler ist aufgetreten, wenn keine JAR-Datei vorhanden war. Ich habe eine Abhängigkeit hinzugefügt, aber die JAR-Datei war im Maven-Repository von "protoc-gen-grpc-java" nicht vorhanden. Nachdem ich verschiedene Dinge ausprobiert hatte, löste ich es, indem ich eine entsprechende JAR-Datei umbenannte und lokal platzierte. Anscheinend hat es eine Abhängigkeit, wurde aber nicht aufgerufen.
Der zweite Punkt scheint sich auf die Klasse "TestRule" von junit im Glas von iroha zu beziehen. Diese Klasse scheint seit Version 4.12 von junit hinzugefügt worden zu sein, und die Version musste 4.12 oder höher sein.
Dies ist der Beispielcode in der Dokumentation.
Example1.java
import iroha.protocol.BlockOuterClass;
import iroha.protocol.Primitive.RolePermission;
import java.math.BigDecimal;
import java.security.KeyPair;
import java.util.Arrays;
import jp.co.soramitsu.crypto.ed25519.Ed25519Sha3;
import jp.co.soramitsu.iroha.testcontainers.IrohaContainer;
import jp.co.soramitsu.iroha.testcontainers.PeerConfig;
import jp.co.soramitsu.iroha.testcontainers.detail.GenesisBlockBuilder;
import lombok.val;
public class Example1 {
private static final String bankDomain = "bank";
private static final String userRole = "user";
private static final String usdName = "usd";
private static final Ed25519Sha3 crypto = new Ed25519Sha3();
private static final KeyPair peerKeypair = crypto.generateKeypair();
private static final KeyPair useraKeypair = crypto.generateKeypair();
private static final KeyPair userbKeypair = crypto.generateKeypair();
private static String user(String name) {
return String.format("%s@%s", name, bankDomain);
}
private static final String usd = String.format("%s#%s", usdName, bankDomain);
/**
* <pre>
* Our initial state cosists of:
* - domain "bank", with default role "user" - can transfer assets and can query their amount
* - asset usd#bank with precision 2
* - user_a@bank, which has 100 usd
* - user_b@bank, which has 0 usd
* </pre>
*/
private static BlockOuterClass.Block getGenesisBlock() {
return new GenesisBlockBuilder()
// first transaction
.addTransaction(
// transactions in genesis block can have no creator
Transaction.builder(null)
// by default peer is listening on port 10001
.addPeer("0.0.0.0:10001", peerKeypair.getPublic())
// create default "user" role
.createRole(userRole,
Arrays.asList(
RolePermission.can_transfer,
RolePermission.can_get_my_acc_ast,
RolePermission.can_get_my_txs,
RolePermission.can_receive
)
)
.createDomain(bankDomain, userRole)
// create user A
.createAccount("user_a", bankDomain, useraKeypair.getPublic())
// create user B
.createAccount("user_b", bankDomain, userbKeypair.getPublic())
// create usd#bank with precision 2
.createAsset(usdName, bankDomain, 2)
// transactions in genesis block can be unsigned
.build() // returns ipj model Transaction
.build() // returns unsigned protobuf Transaction
)
// we want to increase user_a balance by 100 usd
.addTransaction(
Transaction.builder(user("user_a"))
.addAssetQuantity(usd, new BigDecimal("100"))
.build()
.build()
)
.build();
}
public static PeerConfig getPeerConfig() {
PeerConfig config = PeerConfig.builder()
.genesisBlock(getGenesisBlock())
.build();
// don't forget to add peer keypair to config
config.withPeerKeyPair(peerKeypair);
return config;
}
/**
* Custom facade over GRPC Query
*/
public static int getBalance(IrohaAPI api, String userId, KeyPair keyPair) {
// build protobuf query, sign it
val q = Query.builder(userId, 1)
.getAccountAssets(userId)
.buildSigned(keyPair);
// execute query, get response
val res = api.query(q);
// get list of assets from our response
val assets = res.getAccountAssetsResponse().getAccountAssetsList();
// find usd asset
val assetUsdOptional = assets
.stream()
.filter(a -> a.getAssetId().equals(usd))
.findFirst();
// numbers are small, so we use int here for simplicity
return assetUsdOptional
.map(a -> Integer.parseInt(a.getBalance()))
.orElse(0);
}
public static void main(String[] args) {
// for simplicity, we will create Iroha peer in place
IrohaContainer iroha = new IrohaContainer()
.withPeerConfig(getPeerConfig());
// start the peer. blocking call
iroha.start();
// create API wrapper
IrohaAPI api = new IrohaAPI(iroha.getToriiAddress());
// transfer 100 usd from user_a to user_b
val tx = Transaction.builder("user_a@bank")
.transferAsset("user_a@bank", "user_b@bank", usd, "For pizza", "10")
.sign(useraKeypair)
.build();
// create transaction observer
// here you can specify any kind of handlers on transaction statuses
val observer = TransactionStatusObserver.builder()
// executed when stateless or stateful validation is failed
.onTransactionFailed(t -> System.out.println(String.format(
"transaction %s failed with msg: %s",
t.getTxHash(),
t.getErrOrCmdName()
)))
// executed when got any exception in handlers or grpc
.onError(e -> System.out.println("Failed with exception: " + e))
// executed when we receive "committed" status
.onTransactionCommitted((t) -> System.out.println("Committed :)"))
// executed when transfer is complete (failed or succeed) and observable is closed
.onComplete(() -> System.out.println("Complete"))
.build();
// blocking send.
// use .subscribe() for async sending
api.transaction(tx)
.blockingSubscribe(observer);
/// now lets query balances
val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
val balanceUserB = getBalance(api, user("user_b"), userbKeypair);
// ensure we got correct balances
assert balanceUserA == 90;
assert balanceUserB == 10;
}
}
Vorerst werde ich es verschieben.
[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] INFO org.testcontainers.dockerclient.DockerMachineClientProviderStrategy - Found docker-machine, and will use machine named default
[main] INFO org.testcontainers.dockerclient.DockerMachineClientProviderStrategy - Docker daemon IP address for docker machine default is 192.168.99.100
[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - NpipeSocketClientProviderStrategy: failed with exception InvalidConfigurationException (ping failed). Root cause TimeoutException (null)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - WindowsClientProviderStrategy: failed with exception TimeoutException (Timeout waiting for result with exception). Root cause ConnectException (Connection refused: connect)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - DockerMachineClientProviderStrategy: failed with exception TimeoutException (Timeout waiting for result with exception). Root cause ConnectException (Connection refused: connect)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - As no valid configuration was found, execution cannot continue
Exception in thread "main" java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
Ja. Ich habe einen Fehler bekommen.
「Could not find a valid Docker environment.」 Dieser Fehler scheint aufzutreten, wenn ich versuche, Docker von Java aus zu betreiben, und die "Docker-Datei" nicht vorhanden ist.
Ich erstelle eine Umgebung mit VirtualBox, nicht mit Docker, also hat es nicht funktioniert.
Wenn Sie das Programm hier richtig lesen, wird Peer hinzugefügt, ein Konto im hinzugefügten Peer erstellt und Asset (Token) gesendet.
Löschen Sie den Teil, der Docker betreibt, aus der Verarbeitung der Hauptanweisung und ändern Sie ihn in "admin @ test" und "test @ test", die standardmäßig auch für den Benutzer erstellt werden.
Example1.java
public static void main(String[] args) throws Exception{
URI uri = new URI(null,null, "IP Adresse",50051,null,null,null);
// create API wrapper
IrohaAPI api = new IrohaAPI(uri);
// transfer 100 usd from user_a to user_b
val tx = Transaction.builder("admin@test")
.transferAsset("admin@test", "test@test", usd, "For pizza", "10")
.sign(useraKeypair)
.build();
// create transaction observer
// here you can specify any kind of handlers on transaction statuses
val observer = TransactionStatusObserver.builder()
// executed when stateless or stateful validation is failed
.onTransactionFailed(t -> System.out.println(String.format(
"transaction %s failed with msg: %s",
t.getTxHash(),
t.getErrOrCmdName()
)))
// executed when got any exception in handlers or grpc
.onError(e -> System.out.println("Failed with exception: " + e))
// executed when we receive "committed" status
.onTransactionCommitted((t) -> System.out.println("Committed :)"))
// executed when transfer is complete (failed or succeed) and observable is closed
.onComplete(() -> System.out.println("Complete"))
.build();
// blocking send.
// use .subscribe() for async sending
api.transaction(tx)
.blockingSubscribe(observer);
/// now lets query balances
val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
val balanceUserB = getBalance(api, user("user_b"), userbKeypair);
// ensure we got correct balances
assert balanceUserA == 90;
assert balanceUserB == 10;
}
Der Punkt, den ich geändert habe, ist der Konstruktor der Klasse "IrohaApi". Dieser Konstruktor verwendet eine URI-Klasse, ruft jedoch nur die Domäne und den Port von der im Konstruktor empfangenen URI-Klasse ab. Erstellen Sie also eine URI-Klasse, die nur die Domäne und den Port festlegt, und legen Sie sie als Argument fest. Wenn Sie dies bisher tun können, führen Sie es aus.
transaction c3d966d1cbc521b99e1cb60cbe444f129947bdfbe348abe7b7262dd81079c505 failed with msg: signatures validation
Complete
Ich habe einen Fehler bekommen, aber es hat vorerst funktioniert. Die Ursache für diesen Fehler ist, dass das zum Signieren der Transaktion verwendete KeyPair nicht von admin @ test stammt.
Ich kenne die Ursache, aber ich untersuche immer noch, wie ich damit umgehen soll, also werde ich sie aktualisieren, sobald ich es herausfinde.
Jetzt, da ich weiß, wie man eine Transaktion sendet, werde ich sie hinzufügen.
Example1.java
public static void main(String[] args) throws Exception{
URI uri = new URI(null,null, "192.168.33.10",50051,null,null,null);
// create API wrapper
IrohaAPI api = new IrohaAPI(uri);
byte[] pubByte = Hex.decodeHex("[email protected] der Kneipe (öffentlicher Schlüssel)");
byte[] privByte = Hex.decodeHex("[email protected] von priv (privater Schlüssel)");
KeyPair adminKeyPair = Ed25519Sha3.keyPairFromBytes(privByte, pubByte);
// transfer 100 usd from user_a to user_b
val tx = Transaction.builder("admin@test")
.transferAsset("admin@test", "test@test", usd, "For pizza", "10")
.sign(adminKeyPair)
.build();
// create transaction observer
// here you can specify any kind of handlers on transaction statuses
val observer = TransactionStatusObserver.builder()
// executed when stateless or stateful validation is failed
.onTransactionFailed(t -> System.out.println(String.format(
"transaction %s failed with msg: %s",
t.getTxHash(),
t.getErrOrCmdName()
)))
// executed when got any exception in handlers or grpc
.onError(e -> System.out.println("Failed with exception: " + e))
// executed when we receive "committed" status
.onTransactionCommitted((t) -> System.out.println("Committed :)"))
// executed when transfer is complete (failed or succeed) and observable is closed
.onComplete(() -> System.out.println("Complete"))
.build();
// blocking send.
// use .subscribe() for async sending
api.transaction(tx)
.blockingSubscribe(observer);
/// now lets query balances
val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
val balanceUserB = getBalance(api, user("user_b"), userbKeypair);
// ensure we got correct balances
assert balanceUserA == 90;
assert balanceUserB == 10;
}
Der Punkt besteht darin, ein KeyPair-Objekt aus dem öffentlichen und dem privaten Schlüssel zu erstellen.
byte[] pubByte = Hex.decodeHex("[email protected] der Kneipe (öffentlicher Schlüssel)");
byte[] privByte = Hex.decodeHex("[email protected] von priv (privater Schlüssel)");
KeyPair adminKeyPair = Ed25519Sha3.keyPairFromBytes(privByte, pubByte);
Setzen Sie dies in das Argument der sign-Methode und Sie sind fertig. Ich werde es versuchen.
Committed :)
Complete
Es endete normal.
Werfen wir einen Blick auf den Inhalt des Blocks. Wenn Sie die Umgebung gemäß dem Verfahren im offiziellen Dokument erstellen, sollte sie im Verzeichnis "/ tmp / block_store" ausgegeben werden. Der Inhalt ist JSON geformt.
{
"blockV1": {
"payload": {
"transactions": [
{
"payload": {
"reducedPayload": {
"commands": [
{
"transferAsset": {
"srcAccountId": "admin@test",
"destAccountId": "test@test",
"assetId": "usd#bank",
"description": "For pizza",
"amount": "10"
}
}
],
"creatorAccountId": "admin@test",
"createdTime": "1570624170543",
"quorum": 1
}
},
"signatures": [
{
"publicKey": "313A07E6384776ED95447710D15E59148473CCFC052A681317A72A69F2A49910",
"signature": "4B2B45A4F2FDB9A7DE6F30E110D8DEA5E5AAB30C40F5685CFA71FDC38E72BF3839954DDA13FE027FEA18DA9F97332E5E265822922204D38F1667D60E5F8E9601"
}
]
}
],
"height": "17",
"prevBlockHash": "ff33c8483725758e09583e7f670b9c37a091357bc027602a72452d44907861f1",
"createdTime": "1570624184188"
},
"signatures": [
{
"publicKey": "bddd58404d1315e0eb27902c5d7c8eb0602c16238f005773df406bc191308929",
"signature": "680ee97a530314e877ee7518b22975607e328a232a60f963029cb07b18b9101d0de6bf174ecd07ab29bac8123e8c47f12761835a121b301f192c0de9a908670e"
}
]
}
}
Ich konnte bestätigen, dass es sicher in den Block importiert wurde.
Ich habe kürzlich Ethereum studiert, damit ich es ohne Bergbau in den Block bringen kann, ja? Aber wenn Sie darüber nachdenken, ist es natürlich.
Selbst wenn ich dieses Beispielprogramm ausführe, habe ich den Eindruck, dass es auf Japanisch nur sehr wenige Informationen gibt. Ich denke darüber nach, eine einfache Wallet-App zu erstellen und Kettencode bereitzustellen, daher sollte ich aktiv Informationen hinterlassen.
Recommended Posts