From 2d4634031d5330c1d63184543c8fb5b67251fef8 Mon Sep 17 00:00:00 2001 From: Yuji Ito Date: Fri, 22 May 2020 15:38:12 +0900 Subject: [PATCH] add verification of DL (#14) --- benchmark-client/config.toml | 34 ---- .../client/transfer/TransferProcessor.java | 50 ----- client-test/benchmark-config.toml | 33 ++++ .../build.gradle | 6 + .../sample-keys/certificate.pem | 0 .../sample-keys/private-key.pem | 0 .../src/main/java}/client/Common.java | 33 +++- .../java/client/transfer/TransferChecker.java | 171 ++++++++++++++++++ .../client/transfer/TransferPreparer.java | 88 ++++----- .../client/transfer/TransferProcessor.java | 115 ++++++++++++ .../client/transfer/TransferReporter.java | 2 +- .../src/main/java/contract/Balance.java | 24 +++ .../src/main/java}/contract/BatchCreate.java | 2 +- .../src/main/java}/contract/Transfer.java | 4 +- client-test/verification-config.toml | 47 +++++ .../java/com/scalar/kelpie/config/Config.java | 41 +++++ .../modules/FrequencyBasedProcessor.java | 4 +- .../kelpie/modules/TimeBasedProcessor.java | 12 +- .../com/scalar/kelpie/config/ConfigTest.java | 30 +++ settings.gradle | 2 +- verification-db/build.gradle | 4 +- verification-db/config.toml | 1 + .../db/transfer/CassandraKiller.java | 2 +- 23 files changed, 559 insertions(+), 146 deletions(-) delete mode 100644 benchmark-client/config.toml delete mode 100644 benchmark-client/src/main/java/benchmark/client/transfer/TransferProcessor.java create mode 100644 client-test/benchmark-config.toml rename {benchmark-client => client-test}/build.gradle (57%) rename {benchmark-client => client-test}/sample-keys/certificate.pem (100%) rename {benchmark-client => client-test}/sample-keys/private-key.pem (100%) rename {benchmark-client/src/main/java/benchmark => client-test/src/main/java}/client/Common.java (57%) create mode 100644 client-test/src/main/java/client/transfer/TransferChecker.java rename {benchmark-client/src/main/java/benchmark => client-test/src/main/java}/client/transfer/TransferPreparer.java (60%) create mode 100644 client-test/src/main/java/client/transfer/TransferProcessor.java rename {benchmark-client/src/main/java/benchmark => client-test/src/main/java}/client/transfer/TransferReporter.java (89%) create mode 100644 client-test/src/main/java/contract/Balance.java rename {benchmark-client/src/main/java/benchmark => client-test/src/main/java}/contract/BatchCreate.java (96%) rename {benchmark-client/src/main/java/benchmark => client-test/src/main/java}/contract/Transfer.java (95%) create mode 100644 client-test/verification-config.toml diff --git a/benchmark-client/config.toml b/benchmark-client/config.toml deleted file mode 100644 index 54da9ac..0000000 --- a/benchmark-client/config.toml +++ /dev/null @@ -1,34 +0,0 @@ -[modules] - [modules.preprocessor] - name = "benchmark.client.transfer.TransferPreparer" - path = "benchmark-client/build/libs/benchmark-client-all.jar" - [modules.processor] - name = "benchmark.client.transfer.TransferProcessor" - path = "benchmark-client/build/libs/benchmark-client-all.jar" - [modules.postprocessor] - name = "benchmark.client.transfer.TransferReporter" - path = "benchmark-client/build/libs/benchmark-client-all.jar" - -[common] - concurrency = 1 - run_for_sec = 200 - ramp_for_sec = 10 - -[stats] - realtime_report_enabled = true - -[test_config] - num_accounts = 100000 - population_concurrency = 32 - -[contract] - population_contract_name = "benchmark.contract.BatchCreate" - population_contract_path = "benchmark-client/build/classes/java/main/benchmark/contract/BatchCreate.class" - transfer_contract_name = "benchmark.contract.Transfer" - transfer_contract_path = "benchmark-client/build/classes/java/main/benchmark/contract/Transfer.class" - -[client_config] - dl_server = "localhost" - # dl_server_port = 50051 - certificate = "benchmark-client/sample-keys/certificate.pem" - private_key = "benchmark-client/sample-keys/private-key.pem" diff --git a/benchmark-client/src/main/java/benchmark/client/transfer/TransferProcessor.java b/benchmark-client/src/main/java/benchmark/client/transfer/TransferProcessor.java deleted file mode 100644 index aaaed73..0000000 --- a/benchmark-client/src/main/java/benchmark/client/transfer/TransferProcessor.java +++ /dev/null @@ -1,50 +0,0 @@ -package benchmark.client.transfer; - -import benchmark.client.Common; -import com.scalar.dl.client.service.ClientService; -import com.scalar.kelpie.config.Config; -import com.scalar.kelpie.modules.TimeBasedProcessor; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; - -public class TransferProcessor extends TimeBasedProcessor { - private final String transferContractName; - private final ClientService service; - private final int numAccounts; - - public TransferProcessor(Config config) { - super(config); - this.service = Common.getClientService(config); - - this.numAccounts = (int) config.getUserLong("test_config", "num_accounts"); - this.transferContractName = config.getUserString("contract", "transfer_contract_name"); - } - - @Override - public void executeEach() { - int fromId = ThreadLocalRandom.current().nextInt(numAccounts); - int toId = ThreadLocalRandom.current().nextInt(numAccounts); - JsonObject arg = makeArgument(fromId, toId); - - service.executeContract(transferContractName, arg); - } - - @Override - public void close() { - service.close(); - } - - private JsonObject makeArgument(int fromId, int toId) { - JsonArray assetIds = - Json.createArrayBuilder().add(String.valueOf(fromId)).add(String.valueOf(toId)).build(); - - return Json.createObjectBuilder() - .add("asset_ids", assetIds) - .add("amount", 1) - .add("nonce", UUID.randomUUID().toString()) - .build(); - } -} diff --git a/client-test/benchmark-config.toml b/client-test/benchmark-config.toml new file mode 100644 index 0000000..d950c18 --- /dev/null +++ b/client-test/benchmark-config.toml @@ -0,0 +1,33 @@ +[modules] + [modules.preprocessor] + name = "client.transfer.TransferPreparer" + path = "client-test/build/libs/client-test-all.jar" + [modules.processor] + name = "client.transfer.TransferProcessor" + path = "client-test/build/libs/client-test-all.jar" + [modules.postprocessor] + name = "client.transfer.TransferReporter" + path = "client-test/build/libs/client-test-all.jar" + +[common] + concurrency = 1 + run_for_sec = 200 + ramp_for_sec = 10 + +[stats] + realtime_report_enabled = true + +[test_config] + num_accounts = 100000 + population_concurrency = 32 + +[contract] + population_contract_name = "contract.BatchCreate" + population_contract_path = "client-test/build/classes/java/main/contract/BatchCreate.class" + transfer_contract_name = "contract.Transfer" + transfer_contract_path = "client-test/build/classes/java/main/contract/Transfer.class" + +[client_config] + dl_server = "localhost" + certificate = "client-test/sample-keys/certificate.pem" + private_key = "client-test/sample-keys/private-key.pem" diff --git a/benchmark-client/build.gradle b/client-test/build.gradle similarity index 57% rename from benchmark-client/build.gradle rename to client-test/build.gradle index 52dba12..5565339 100644 --- a/benchmark-client/build.gradle +++ b/client-test/build.gradle @@ -10,4 +10,10 @@ repositories { dependencies { compileOnly project(':kelpie') implementation group: 'com.scalar-labs', name: 'scalardl-java-client-sdk', version: '2.0.5' + implementation group: 'com.scalar-labs', name: 'scalardb', version: '2.0.1' + implementation group: "io.github.resilience4j", name: "resilience4j-retry", version: "1.3.1" +} + +shadowJar { + exclude 'contract/*' } diff --git a/benchmark-client/sample-keys/certificate.pem b/client-test/sample-keys/certificate.pem similarity index 100% rename from benchmark-client/sample-keys/certificate.pem rename to client-test/sample-keys/certificate.pem diff --git a/benchmark-client/sample-keys/private-key.pem b/client-test/sample-keys/private-key.pem similarity index 100% rename from benchmark-client/sample-keys/private-key.pem rename to client-test/sample-keys/private-key.pem diff --git a/benchmark-client/src/main/java/benchmark/client/Common.java b/client-test/src/main/java/client/Common.java similarity index 57% rename from benchmark-client/src/main/java/benchmark/client/Common.java rename to client-test/src/main/java/client/Common.java index 55ac60d..b801578 100644 --- a/benchmark-client/src/main/java/benchmark/client/Common.java +++ b/client-test/src/main/java/client/Common.java @@ -1,4 +1,4 @@ -package benchmark.client; +package client; import com.google.inject.Guice; import com.google.inject.Injector; @@ -6,6 +6,10 @@ import com.scalar.dl.client.service.ClientModule; import com.scalar.dl.client.service.ClientService; import com.scalar.kelpie.config.Config; +import io.github.resilience4j.core.IntervalFunction; +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; +import java.time.Duration; import java.util.Properties; public class Common { @@ -13,6 +17,11 @@ public class Common { private static String PORT = "50051"; private static ClientConfig config; private static final String CERT_HOLDER_ID = "test_holder"; + private static final int MAX_RETRIES = 10; + private static final Duration WAIT_DURATION = Duration.ofMillis(1000); + private static final long SLEEP_BASE_MILLIS = 100L; + + public static final int INITIAL_BALANCE = 10000; public static ClientConfig getClientConfig(Config config) { String host = config.getUserString("client_config", "dl_server", HOST); @@ -36,4 +45,26 @@ public static ClientService getClientService(Config config) { return injector.getInstance(ClientService.class); } + + public static int getTotalInitialBalance(Config config) { + int numAccounts = (int) config.getUserLong("test_config", "num_accounts"); + + return INITIAL_BALANCE * numAccounts; + } + + public static Retry getRetryWithFixedWaitDuration(String name) { + RetryConfig retryConfig = + RetryConfig.custom().maxAttempts(MAX_RETRIES).waitDuration(WAIT_DURATION).build(); + + return Retry.of(name, retryConfig); + } + + public static Retry getRetryWithExponentialBackoff(String name) { + IntervalFunction intervalFunc = IntervalFunction.ofExponentialBackoff(SLEEP_BASE_MILLIS, 2.0); + + RetryConfig retryConfig = + RetryConfig.custom().maxAttempts(MAX_RETRIES).intervalFunction(intervalFunc).build(); + + return Retry.of(name, retryConfig); + } } diff --git a/client-test/src/main/java/client/transfer/TransferChecker.java b/client-test/src/main/java/client/transfer/TransferChecker.java new file mode 100644 index 0000000..da9dc0a --- /dev/null +++ b/client-test/src/main/java/client/transfer/TransferChecker.java @@ -0,0 +1,171 @@ +package client.transfer; + +import client.Common; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.scalar.db.api.TransactionState; +import com.scalar.db.config.DatabaseConfig; +import com.scalar.db.exception.transaction.CoordinatorException; +import com.scalar.db.service.StorageModule; +import com.scalar.db.service.StorageService; +import com.scalar.db.transaction.consensuscommit.Coordinator; +import com.scalar.dl.client.service.ClientService; +import com.scalar.kelpie.config.Config; +import com.scalar.kelpie.exception.PostProcessException; +import com.scalar.kelpie.modules.PostProcessor; +import io.github.resilience4j.retry.Retry; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; + +public class TransferChecker extends PostProcessor { + + public TransferChecker(Config config) { + super(config); + } + + @Override + public void execute() { + List results = readBalancesWithRetry(); + + int committed = getNumOfCommittedFromCoordinator(); + + if (!isConsistent(results, committed)) { + throw new PostProcessException("Inconsistency happened!"); + } + } + + private List readBalancesWithRetry() { + logInfo("reading latest assets..."); + + Retry retry = Common.getRetryWithExponentialBackoff("readBalances"); + Supplier> decorated = Retry.decorateSupplier(retry, this::readBalances); + + try { + return decorated.get(); + } catch (Exception e) { + throw new PostProcessException("Reading records failed repeatedly", e); + } + } + + @Override + public void close() {} + + private List readBalances() { + int numAccounts = (int) config.getUserLong("test_config", "num_accounts"); + List results = new ArrayList<>(); + + boolean isFailed = false; + ClientService service = Common.getClientService(config); + String name = config.getUserString("contract", "balance_contract_name"); + + for (int i = 0; i < numAccounts; i++) { + try { + JsonObject argument = + Json.createObjectBuilder() + .add("asset_id", String.valueOf(i)) + .add("nonce", UUID.randomUUID().toString()) + .build(); + + JsonObject result = service.executeContract(name, argument).getResult().get(); + results.add(result); + } catch (Exception e) { + // continue to read other records + isFailed = true; + } + } + + if (isFailed) { + // for Retry + throw new RuntimeException("at least 1 record couldn't be read"); + } + + return results; + } + + private int getNumOfCommittedFromCoordinator() { + Coordinator coordinator = getCoordinator(); + Retry retry = Common.getRetryWithExponentialBackoff("checkCoordinator"); + Function> decorated = + Retry.decorateFunction(retry, id -> getState(coordinator, id)); + + JsonObject unknownTransactions = getPreviousState().getJsonObject("unknown_transaction"); + int committed = 0; + for (String txId : unknownTransactions.keySet()) { + Optional state; + try { + state = decorated.apply(txId); + } catch (Exception e) { + throw new PostProcessException("Reading the status failed repeatedly", e); + } + if (state.isPresent() && state.get().getState().equals(TransactionState.COMMITTED)) { + JsonArray ids = unknownTransactions.getJsonArray(txId); + logInfo( + "id: " + + txId + + " from: " + + ids.getInt(0) + + " to: " + + ids.getInt(1) + + " succeeded, not failed"); + committed++; + } + } + + return committed; + } + + private Coordinator getCoordinator() { + Properties props = new Properties(); + String contactPoints = config.getUserString("test_config", "contact_points"); + props.setProperty("scalar.db.contact_points", contactPoints); + props.setProperty("scalar.db.username", "cassandra"); + props.setProperty("scalar.db.password", "cassandra"); + + DatabaseConfig dbConfig = new DatabaseConfig(props); + Injector injector = Guice.createInjector(new StorageModule(dbConfig)); + StorageService storage = injector.getInstance(StorageService.class); + + return new Coordinator(storage); + } + + private Optional getState(Coordinator coordinator, String txId) { + try { + logInfo("reading the status of " + txId); + + return coordinator.getState(txId); + } catch (CoordinatorException e) { + // convert the exception for Retry + throw new RuntimeException("Failed to read the state from the coordinator", e); + } + } + + private boolean isConsistent(List results, int committed) { + int totalVersion = results.stream().mapToInt(r -> r.getInt("age")).sum(); + int totalBalance = results.stream().mapToInt(r -> r.getInt("balance")).sum(); + int expectedTotalVersion = ((int) getStats().getSuccessCount() + committed) * 2; + int expectedTotalBalance = Common.getTotalInitialBalance(config); + + logInfo("total version: " + totalVersion); + logInfo("expected total version: " + expectedTotalVersion); + logInfo("total balance: " + totalBalance); + logInfo("expected total balance: " + expectedTotalBalance); + + if (totalVersion != expectedTotalVersion) { + logError("version mismatch !"); + return false; + } + if (totalBalance != expectedTotalBalance) { + logError("balance mismatch !"); + return false; + } + return true; + } +} diff --git a/benchmark-client/src/main/java/benchmark/client/transfer/TransferPreparer.java b/client-test/src/main/java/client/transfer/TransferPreparer.java similarity index 60% rename from benchmark-client/src/main/java/benchmark/client/transfer/TransferPreparer.java rename to client-test/src/main/java/client/transfer/TransferPreparer.java index a5fb7b4..0ba3d0c 100644 --- a/benchmark-client/src/main/java/benchmark/client/transfer/TransferPreparer.java +++ b/client-test/src/main/java/client/transfer/TransferPreparer.java @@ -1,10 +1,11 @@ -package benchmark.client.transfer; +package client.transfer; -import benchmark.client.Common; +import client.Common; import com.scalar.dl.client.service.ClientService; import com.scalar.kelpie.config.Config; import com.scalar.kelpie.exception.PreProcessException; import com.scalar.kelpie.modules.PreProcessor; +import io.github.resilience4j.retry.Retry; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -19,20 +20,26 @@ public class TransferPreparer extends PreProcessor { private final long POPULATION_CONCURRENCY = 16L; private final int NUM_ACCOUNTS_PER_TX = 100; - private final int INITIAL_BALANCE = 10000; private final String populationContractName; private final String populationContractPath; private final String transferContractName; private final String transferContractPath; + private final String balanceContractName; + private final String balanceContractPath; + private final boolean isVerification; public TransferPreparer(Config config) { super(config); - populationContractName = config.getUserString("contract", "population_contract_name"); - populationContractPath = config.getUserString("contract", "population_contract_path"); - transferContractName = config.getUserString("contract", "transfer_contract_name"); - transferContractPath = config.getUserString("contract", "transfer_contract_path"); + this.populationContractName = config.getUserString("contract", "population_contract_name"); + this.populationContractPath = config.getUserString("contract", "population_contract_path"); + this.transferContractName = config.getUserString("contract", "transfer_contract_name"); + this.transferContractPath = config.getUserString("contract", "transfer_contract_path"); + + this.isVerification = config.getUserBoolean("test_config", "is_verification", false); + this.balanceContractName = config.getUserString("contract", "balance_contract_name", ""); + this.balanceContractPath = config.getUserString("contract", "balance_contract_path", ""); } @Override @@ -52,6 +59,10 @@ private void registerCertificateAndContracts() { populationContractName, populationContractName, populationContractPath, Optional.empty()); service.registerContract( transferContractName, transferContractName, transferContractPath, Optional.empty()); + if (isVerification) { + service.registerContract( + balanceContractName, balanceContractName, balanceContractPath, Optional.empty()); + } } catch (Exception e) { throw new PreProcessException("Preparation failed a service", e); } @@ -98,49 +109,38 @@ public void run() { return; } - IntStream.range(0, (numPerThread + NUM_ACCOUNTS_PER_TX - 1) / NUM_ACCOUNTS_PER_TX) - .forEach( - i -> { - int startId = start + NUM_ACCOUNTS_PER_TX * i; - int endId = Math.min(start + NUM_ACCOUNTS_PER_TX * (i + 1), end); - populateWithTx(startId, endId); - }); - try { - service.close(); + IntStream.range(0, (numPerThread + NUM_ACCOUNTS_PER_TX - 1) / NUM_ACCOUNTS_PER_TX) + .forEach( + i -> { + int startId = start + NUM_ACCOUNTS_PER_TX * i; + int endId = Math.min(start + NUM_ACCOUNTS_PER_TX * (i + 1), end); + populateWithTx(startId, endId); + }); } catch (Exception e) { - throw new PreProcessException("Failed to shutdown a service", e); + throw new PreProcessException("Population failed", e); + } finally { + service.close(); } } private void populateWithTx(int startId, int endId) { - int retries = 0; - while (true) { - if (retries++ > 10) { - logError("population failed repeatedly!"); - try { - service.close(); - } catch (Exception e) { - logError("service close failed"); - throw e; - } - } - try { - JsonObject argument = - Json.createObjectBuilder() - .add("start_id", startId) - .add("end_id", endId) - .add("amount", INITIAL_BALANCE) - .add("nonce", UUID.randomUUID().toString()) - .build(); - - service.executeContract(populationContractName, argument); - - // success - break; - } catch (Exception e) { - logWarn("population failed, retry"); - } + JsonObject argument = + Json.createObjectBuilder() + .add("start_id", startId) + .add("end_id", endId) + .add("amount", Common.INITIAL_BALANCE) + .add("nonce", UUID.randomUUID().toString()) + .build(); + Runnable populate = () -> service.executeContract(populationContractName, argument); + + Retry retry = Common.getRetryWithFixedWaitDuration("populate"); + Runnable decorated = Retry.decorateRunnable(retry, populate); + try { + decorated.run(); + } catch (Exception e) { + logError("population failed repeatedly!"); + throw e; } } } diff --git a/client-test/src/main/java/client/transfer/TransferProcessor.java b/client-test/src/main/java/client/transfer/TransferProcessor.java new file mode 100644 index 0000000..0ce4191 --- /dev/null +++ b/client-test/src/main/java/client/transfer/TransferProcessor.java @@ -0,0 +1,115 @@ +package client.transfer; + +import client.Common; +import com.scalar.dl.client.exception.ClientException; +import com.scalar.dl.client.service.ClientService; +import com.scalar.dl.ledger.service.StatusCode; +import com.scalar.kelpie.config.Config; +import com.scalar.kelpie.modules.TimeBasedProcessor; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; + +public class TransferProcessor extends TimeBasedProcessor { + private final String transferContractName; + private final ClientService service; + private final int numAccounts; + private final boolean isVerification; + + // for verification + private final Map> unknownTransactions = new ConcurrentHashMap<>(); + + public TransferProcessor(Config config) { + super(config); + this.service = Common.getClientService(config); + + this.numAccounts = (int) config.getUserLong("test_config", "num_accounts"); + this.transferContractName = config.getUserString("contract", "transfer_contract_name"); + this.isVerification = config.getUserBoolean("test_config", "is_verification", false); + } + + @Override + public void executeEach() { + int fromId = ThreadLocalRandom.current().nextInt(numAccounts); + int toId = ThreadLocalRandom.current().nextInt(numAccounts); + int amount = ThreadLocalRandom.current().nextInt(1000) + 1; + JsonObject arg = makeArgument(fromId, toId, amount); + + String txId = arg.getString("nonce"); + logStart(txId, fromId, toId, amount); + + try { + service.executeContract(transferContractName, arg); + } catch (Exception e) { + logFailure(txId, fromId, toId, amount, e); + throw e; + } + + logSuccess(txId, fromId, toId, amount); + } + + @Override + public void close() { + if (isVerification) { + JsonObjectBuilder builder = Json.createObjectBuilder(); + unknownTransactions.forEach( + (txId, ids) -> { + builder.add(txId, Json.createArrayBuilder().add(ids.get(0)).add(ids.get(1)).build()); + }); + + setState(Json.createObjectBuilder().add("unknown_transaction", builder.build()).build()); + } + + service.close(); + } + + private JsonObject makeArgument(int fromId, int toId, int amount) { + JsonArray assetIds = + Json.createArrayBuilder().add(String.valueOf(fromId)).add(String.valueOf(toId)).build(); + + return Json.createObjectBuilder() + .add("asset_ids", assetIds) + .add("amount", amount) + .add("nonce", UUID.randomUUID().toString()) + .build(); + } + + private void logStart(String txId, int fromId, int toId, int amount) { + if (isVerification) { + logTxInfo("started", txId, fromId, toId, amount); + } + } + + private void logSuccess(String txId, int fromId, int toId, int amount) { + if (isVerification) { + logTxInfo("succeeded", txId, fromId, toId, amount); + } + } + + private void logFailure(String txId, int fromId, int toId, int amount, Throwable e) { + if (!isVerification) { + return; + } + + if ((e instanceof ClientException) + && (((ClientException) e).getStatusCode() == StatusCode.UNKNOWN_TRANSACTION_STATUS)) { + unknownTransactions.put(txId, Arrays.asList(fromId, toId)); + logWarn("the status of the transaction is unknown: " + txId, e); + logTxInfo("unknown", txId, fromId, toId, amount); + } else { + logWarn(txId + " failed", e); + logTxInfo("failed", txId, fromId, toId, amount); + } + } + + private void logTxInfo(String status, String txId, int fromId, int toId, int amount) { + logInfo(status + " - id: " + txId + " from: " + fromId + " to: " + toId + " amount: " + amount); + } +} diff --git a/benchmark-client/src/main/java/benchmark/client/transfer/TransferReporter.java b/client-test/src/main/java/client/transfer/TransferReporter.java similarity index 89% rename from benchmark-client/src/main/java/benchmark/client/transfer/TransferReporter.java rename to client-test/src/main/java/client/transfer/TransferReporter.java index e0940b9..8c68fd6 100644 --- a/benchmark-client/src/main/java/benchmark/client/transfer/TransferReporter.java +++ b/client-test/src/main/java/client/transfer/TransferReporter.java @@ -1,4 +1,4 @@ -package benchmark.client.transfer; +package client.transfer; import com.scalar.kelpie.config.Config; import com.scalar.kelpie.modules.PostProcessor; diff --git a/client-test/src/main/java/contract/Balance.java b/client-test/src/main/java/contract/Balance.java new file mode 100644 index 0000000..01f538d --- /dev/null +++ b/client-test/src/main/java/contract/Balance.java @@ -0,0 +1,24 @@ +package contract; + +import com.scalar.dl.ledger.asset.Asset; +import com.scalar.dl.ledger.contract.Contract; +import com.scalar.dl.ledger.database.Ledger; +import java.util.Optional; +import javax.json.Json; +import javax.json.JsonObject; + +public class Balance extends Contract { + + @Override + public JsonObject invoke(Ledger ledger, JsonObject argument, Optional properties) { + String assetId = argument.getString("asset_id"); + + Asset asset = ledger.get(assetId).get(); + + return Json.createObjectBuilder() + .add("result", "OK") + .add("age", asset.age()) + .add("balance", asset.data().getInt("balance")) + .build(); + } +} diff --git a/benchmark-client/src/main/java/benchmark/contract/BatchCreate.java b/client-test/src/main/java/contract/BatchCreate.java similarity index 96% rename from benchmark-client/src/main/java/benchmark/contract/BatchCreate.java rename to client-test/src/main/java/contract/BatchCreate.java index f63fe78..394b164 100644 --- a/benchmark-client/src/main/java/benchmark/contract/BatchCreate.java +++ b/client-test/src/main/java/contract/BatchCreate.java @@ -1,4 +1,4 @@ -package benchmark.contract; +package contract; import com.scalar.dl.ledger.contract.Contract; import com.scalar.dl.ledger.database.Ledger; diff --git a/benchmark-client/src/main/java/benchmark/contract/Transfer.java b/client-test/src/main/java/contract/Transfer.java similarity index 95% rename from benchmark-client/src/main/java/benchmark/contract/Transfer.java rename to client-test/src/main/java/contract/Transfer.java index 2487a01..6714d39 100644 --- a/benchmark-client/src/main/java/benchmark/contract/Transfer.java +++ b/client-test/src/main/java/contract/Transfer.java @@ -1,4 +1,4 @@ -package benchmark.contract; +package contract; import com.scalar.dl.ledger.asset.Asset; import com.scalar.dl.ledger.contract.Contract; @@ -17,7 +17,7 @@ public JsonObject invoke(Ledger ledger, JsonObject argument, Optional