[JAVA] Ich habe versucht herauszufinden, wie man einen intelligenten Vertrag für das verteilte Hauptbuch Scalar DLT schreibt.

Dieses Mal möchte ich erklären, wie ein intelligenter Vertrag und Beispielanwendungen geschrieben werden, die in der nächsten und nachfolgenden Zeit verwendet werden. Die Artikel bis zum letzten Mal sind wie folgt.

Asset Management-Anwendung

Obwohl es sich um eine Beispielanwendung handelt, möchte ich Folgendes verwenden, das ich zuvor kurz vorgestellt habe.

Diese Anwendung verwaltet Assets sehr einfach und die folgenden 6 intelligenten Verträge werden vorbereitet.

Vertragsname Funktion
AddTypeContract Registrieren Sie den Asset-Typ
ListTypeContract Holen Sie sich eine Liste der Asset-Typen
AddAssetContract Vermögenswerte registrieren
ListContract Holen Sie sich eine Liste der Assets
StateChangeContract Ändern Sie den Status eines Assets
AssetHistoryContract Abrufen des Asset-Nutzungsverlaufs

Mit diesem intelligenten Vertrag können Sie Vermögenswerte registrieren, anzeigen, ausleihen und zurückgeben. Ich möchte den Inhalt jeder Quelle sofort vorstellen, aber vorher wollen wir die Grundlagen intelligenter Verträge erläutern.

So schreiben Sie einen intelligenten Vertrag

Skalare DL-Smart-Verträge können in reinem Java geschrieben werden. Es ist jedoch nicht gut, es so zu schreiben, wie Sie möchten, daher werde ich die Richtlinien vorstellen.

Es gibt eine Einschränkung, dass die externe Bibliothek nicht verwendet werden kann, dies ist jedoch kein Problem, da nur die Seite des intelligenten Vertrags eine solche eindeutige Verarbeitung durchführen muss. Schauen wir uns auch den Basiscode an.

import com.scalar.ledger.contract.Contract;
import com.scalar.ledger.ledger.Ledger;
import java.util.Optional;
import javax.json.Json;
import javax.json.JsonObject;

public class MyContract extends Contract {
  @Override
  public JsonObject invoke(Ledger ledger, JsonObject argument, Optional<JsonObject> property) {
    // read or write data and respond to clients here
  }
}

Es wird nur die Methode "invoke ()" beschrieben. Da dies jedoch der tatsächlich aufgerufene Teil ist, muss er implementiert werden. Das Argument "Ledger" ist ein Objekt für den Zugriff auf Daten vom Typ "Schlüsselwert", die in Scalar DB gespeichert sind. Daten können hinzugefügt / aktualisiert / gelesen / erfasst werden. argument ist ein Parameter, der an den Smart Contract übergeben wird und im JSON-Format vorliegt. Es wird empfohlen, für dieses Objekt eine Zeichenfolgeeigenschaft mit dem Namen "nonce" festzulegen. Es wird gesagt, dass ein Wert, der auf der Welt einzigartig ist, gut ist, daher denke ich, dass es besser ist, UUID zu verwenden. property ist der Parametersatz bei der Bereitstellung des Smart Contract. Dies kann später nicht mehr geändert werden.

Lassen Sie uns auch ein wenig über die Methode des "Ledgers" erklären. Das erste ist "Optional get (String key)", mit dem der aktuelle Wert des angegebenen Schlüssels abgerufen wird.

Optional<Asset> opt = ledger.get("somekey");
if (opt.isPresent()) {
  Asset asset = opt.get();
  int age = asset.age();
  JsonObject value = asset.data();
}

Das "Alter", das hier abgerufen werden kann, gibt an, wie oft die Daten aktualisiert wurden, und wird automatisch von 0 gezählt. Als nächstes geht es um "void put (String key, JsonObject value)", das ein JSON-Objekt mit dem angegebenen Schlüssel registriert.

Optional<Asset> opt = ledger.get(name);
if (opt.isPresent()) {
  return Json.createObjectBuilder()
    .add("message", "Type " + name + " is already registered.")
    .build();
}

ledger.put(name, Json.createObjectBuilder().build());

Implizites Schreiben ist für "Put" nicht zulässig. Stellen Sie daher sicher, dass Sie vor dem Schreiben "Get" erhalten. Als nächstes geht es um "List scan (String key)". Es kann den Änderungsverlauf des angegebenen Schlüssels abrufen.

AssetFilter filter = new AssetFilter(key);
List<Asset> history = ledger.scan(filter);
for (Asset asset : history) {
  String id = asset.id();
  int age = asset.age();
  JsonObject value = asset.data();
}

Plötzlich kam "AssetFilter" heraus, aber dies ist eine Klasse, mit der Sie den Bereich des Verlaufs angeben können.

//Alter von 5 bis 9 Jahren
new AssetFilter(id).withStartVersion(5, true).withEndVersion(10, false);
//Alter von 5 bis spätestens
new AssetFilter(id).withStartVersion(5, true);

Und soweit ist der grundlegende Teil. Es ist ein bisschen lang her, aber wenn Sie dies berücksichtigen, können Sie die Beschreibung intelligenter Verträge leicht spüren.

Intelligenter Vertrag für Asset Management-Anwendungen

AddTypeContract Bei AddTypeContract wird der Asset-Typ registriert. Die gesamte Quelle ist hier Sie können es unter überprüfen, daher möchte ich hier die Punkte extrahieren und erläutern.

  public static final String HOLDER_ID = "holderId";

Die erste ist eine wichtige Eigenschaft in dieser Asset-Management-Anwendung. Jeder Smart-Vertrag verfügt über eine Eigenschaft namens "Inhaber-ID", die nur zur Bereitstellungszeit festgelegt werden kann.

    if (!property.isPresent() || !property.get().containsKey(HOLDER_ID)) {
      throw new ContractContextException("property: `" + HOLDER_ID + "` is mandatory.");
    }

Wenn der Smart-Vertrag aufgerufen wird, wird die Eigenschaft wie oben beschrieben überprüft, um die Ausführung durch andere Personen als den bereitgestellten Benutzer zu verhindern. Der Rest ist einfach und der gleiche Name wird überprüft.

    Optional<Asset> type = ledger.get(holderId + "-" + name);
    if (type.isPresent()) {
      return Json.createObjectBuilder()
          .add(RESULT, FAILURE)
          .add(MESSAGE, "Type " + name + " is already registered.")
          .build();
    }

    ledger.put(holderId + "-" + name, Json.createObjectBuilder().build());

Und schließlich wird der Asset-Typ zum Wert des Schlüssels von "Inhaber-ID +" - "+ TYP" hinzugefügt.

    JsonObject newType = Json.createObjectBuilder().add(NAME, name).build();
    ledger.get(holderId + "-" + TYPE);
    ledger.put(holderId + "-" + TYPE, newType);

ListTypeContract ListTypeContract ist ein Prozess, der eine Liste der registrierten Asset-Typen zurückgibt. Die Quelle ist hier Ich werde.

Da die Verarbeitung von "Inhaber-ID" üblich ist, ist die Verarbeitung einfach, wenn Sie die Erklärung weglassen, da es sich nur um Datenerfassung handelt.

    AssetFilter filter = new AssetFilter(holderId + "-" + TYPE);
    List<Asset> history = ledger.scan(filter);
    if (history.isEmpty()) {
      return Json.createObjectBuilder()
          .add(RESULT, FAILURE)
          .add(MESSAGE, "No types were registered. Use am add-type to create one.")
          .build();
    }

    JsonArrayBuilder builder = Json.createArrayBuilder();
    for (Asset h : history) {
      JsonObject type =
          Json.createObjectBuilder().add(TYPE, h.data().getString(NAME)).add(AGE, h.age()).build();
      builder.add(type);
    }
    JsonArray types = builder.build();

Wenn keine Daten vorhanden sind, wird ein Fehler zurückgegeben, und wenn Daten vorhanden sind, werden diese als Array zurückgegeben.

AddAssetContract Der folgende AddAssetContract ähnelt AddTypeContract, registriert jedoch ein Asset nach Typ. Die Quelle ist hier.

Es ist im Grunde dasselbe wie AddTypeContract, hat jedoch viele erforderliche Argumente.

    if (!argument.containsKey(TYPE)
        || !argument.containsKey(ASSET)
        || !argument.containsKey(TIMESTAMP)
        || !argument.containsKey(ID)) {
      throw new ContractContextException("wrong argument.");
    }

Ich werde die Prüfung des Typs und der Existenz von Vermögenswerten nicht erläutern, aber danach speichern Sie den Kreditstatus und den Namen.

    JsonObject assetStatusJson =
        Json.createObjectBuilder().add(TIMESTAMP, timestamp).add(STATUS, IN_STOCK).build();
    ledger.put(holderId + "-" + id, assetStatusJson);

    JsonObject assetNameJson = Json.createObjectBuilder().add(ID, id).add(NAME, name).build();
    ledger.put(holderId + "-" + type, assetNameJson);

ListContract Wie Sie sich vorstellen können, ist ListContract im Grunde dasselbe wie ListTypeContract. Dies ist hier Wird sein.

Im Vergleich zu ListTypeContract ist das Argument "type" erforderlich und gibt eine Liste der Assets des angegebenen "type" zurück. Der Grund, warum die Verarbeitung etwas länger dauert, besteht darin, dass der Kreditstatus von einer anderen Verarbeitung erfasst wird.

    JsonArrayBuilder assetsBuilder = Json.createArrayBuilder();
    for (Asset asset : assetList) {
      JsonObject data = asset.data();
      if (data.size() == 0) { // initiated one, ignore it
        continue;
      }
      String id = data.getString(ID);
      String name = data.getString(NAME);

      Optional<Asset> borrowingStatus = ledger.get(holderId + "-" + id);
      if (!borrowingStatus.isPresent()) {
        /**
         * Abnormal case. We found an asset in list but no borrowing status record. Just ignore it
         */
        continue;
      }

      JsonObjectBuilder statusBuilder = Json.createObjectBuilder();
      statusBuilder
          .add(ID, id)
          .add(NAME, name)
          .add(TIMESTAMP, borrowingStatus.get().data().getJsonNumber(TIMESTAMP).longValue())
          .add(STATUS, borrowingStatus.get().data().getString(STATUS));

      if (borrowingStatus.get().data().containsKey(HOLDER_ID)) {
        statusBuilder.add(HOLDER_ID, borrowingStatus.get().data().getString(HOLDER_ID));
      }

      assetsBuilder.add(statusBuilder.build());
    }

StateChangeContract Der folgende StateChangeContract ist der Hauptteil dieser Anwendung. Verantwortlich für die Verwaltung des Ausleih- / Rückgabestatus. Die Quelle ist hier.

Der Vorgang ist einfach, auch wenn er als main bezeichnet wird. Wenn derselbe Status wie der aktuelle angegeben wird, wird ein Fehler zurückgegeben.

    if (data.getString(STATUS).equals(newStatus)) {
      return Json.createObjectBuilder()
          .add(RESULT, FAILURE)
          .add(
              MESSAGE,
              String.format(
                  "Asset is already %s.", ON_LOAN.equals(newStatus) ? "borrowed" : "returned"))
          .build();
    }

Der Rest ist der Prozess der Änderung des Status zum Zeitpunkt der Ausleihe / Rückgabe.

    JsonObjectBuilder newDataBuilder = Json.createObjectBuilder();
    if (newStatus.equals(ON_LOAN)) {
      newDataBuilder.add(HOLDER_ID, holderId).add(TIMESTAMP, timestamp).add(STATUS, newStatus);
    } else if (newStatus.equals(IN_STOCK)) {
      if (!data.containsKey(HOLDER_ID)) {
        return Json.createObjectBuilder()
            .add(RESULT, FAILURE)
            .add(MESSAGE, "Can not return asset without holderId")
            .build();
      }
      if (!data.getString(HOLDER_ID).equals(holderId)) {
        return Json.createObjectBuilder()
            .add(RESULT, FAILURE)
            .add(MESSAGE, "Can not return asset borrowed by another user")
            .build();
      }
      newDataBuilder.add(TIMESTAMP, timestamp).add(STATUS, newStatus);
    }
    ledger.put(holderId + "-" + id, newDataBuilder.build());

Hier wird die Prüfung mit "Inhaber-ID" durchgeführt, und eine Warnmeldung wird auch ausgegeben, wenn der Benutzer anders ist.

AssetHistoryContract Der endgültige AssetHistoryContract ist ein Prozess, der den bisherigen Kreditverlauf durch Angabe der ID des Assets zurückgibt. Die Quelle ist hier Ich werde.

Es ist eine Spezifikation, die einen Fehler zurückgibt, wenn kein Verlauf vorhanden ist.

    AssetFilter filter = new AssetFilter(holderId + "-" + id);
    List<Asset> borrowingHistory = ledger.scan(filter);

    if (borrowingHistory.isEmpty()) {
      return Json.createObjectBuilder()
          .add(RESULT, FAILURE)
          .add(MESSAGE, "This asset is not registered")
          .build();
    }

Wenn es einen Verlauf gibt, wird dieser als Array zurückgegeben.

    JsonArrayBuilder builder = Json.createArrayBuilder();
    JsonObjectBuilder borrowingRecordBuilder = Json.createObjectBuilder();
    for (Asset history : borrowingHistory) {
      borrowingRecordBuilder
          .add(TIMESTAMP, history.data().getJsonNumber(TIMESTAMP).longValue())
          .add(STATUS, history.data().getString(STATUS))
          .add(AGE, history.age());

      if (history.data().containsKey(HOLDER_ID)) {
        borrowingRecordBuilder.add(HOLDER_ID, history.data().getString(HOLDER_ID));
      }
      builder.add(borrowingRecordBuilder.build());
    }

Zusammenfassung

Es war also eine Erklärung, wie man einen intelligenten Vertrag schreibt und woher eine Asset-Management-Anwendung stammt. Ab dem nächsten Mal möchte ich darauf eingehen, wie der Emulator mit dieser Quelle verwendet wird.

Recommended Posts

Ich habe versucht herauszufinden, wie man einen intelligenten Vertrag für das verteilte Hauptbuch Scalar DLT schreibt.
Ich habe das kürzlich besprochene verteilte Hauptbuch Scalar DLT (Smart Contract Execution) untersucht.
Ich habe versucht, die Asset-Management-Anwendung mit dem Emulator des verteilten Hauptbuchs Scalar DLT zu berühren
So schreiben Sie einen Komponententest für Spring Boot 2
Wie schreibe ich Rails
Wie schreibe ich Docker-Compose
Wie schreibe ich Mockito
So schreiben Sie eine Migrationsdatei
Untersucht, wie Dienste mit Watson SDK für Java aufgerufen werden
Wie man guten Code schreibt
Wie schreibe ich einen Java-Kommentar
[Refactoring] So schreiben Sie Routing
Wie schreibe ich Junit 5 organisiert
Wie schreibe ich Rails Seed
Wie schreibe ich Rails Routing
[R Spec on Rails] So schreiben Sie Testcode für Anfänger von Anfängern