diff --git a/.test-env b/.test-env new file mode 100644 index 000000000..3b17bb191 --- /dev/null +++ b/.test-env @@ -0,0 +1,14 @@ +# Configs for testing repo download: +SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" +SDK_TESTING_BRANCH="master" +SDK_TESTING_HARNESS="test-harness" + +VERBOSE_HARNESS=0 + +# WARNING: If set to 1, new features will be LOST when downloading the test harness. +# REGARDLESS: modified features are ALWAYS overwritten. +REMOVE_LOCAL_FEATURES=0 + +# WARNING: Be careful when turning on the next variable. +# In that case you'll need to provide all variables expected by `algorand-sdk-testing`'s `.env` +OVERWRITE_TESTING_ENVIRONMENT=0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 55cb252fb..c2784a93e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +# 1.18.0 + +## What's Changed +### Bugfixes +* Bug-Fix: Pass verbosity to the harness and sandbox by @tzaffi in https://github.com/algorand/java-algorand-sdk/pull/371 +### New Features +* StateProofs: Add State Proof support. by @winder in https://github.com/algorand/java-algorand-sdk/pull/360 +### Enhancements +* Enhancement: Use Sandbox for Testing by @tzaffi in https://github.com/algorand/java-algorand-sdk/pull/363 +* Enhancement: Deprecating use of langspec by @ahangsu in https://github.com/algorand/java-algorand-sdk/pull/367 + +## New Contributors +* @tzaffi made their first contribution in https://github.com/algorand/java-algorand-sdk/pull/363 + +**Full Changelog**: https://github.com/algorand/java-algorand-sdk/compare/1.17.0...1.18.0 + +# 1.18.0-beta-1 +## What's Changed +### Bugfixes +* Bug-Fix: Pass verbosity to the harness and sandbox by @tzaffi in https://github.com/algorand/java-algorand-sdk/pull/371 +### New Features +* StateProofs: Add State Proof support. by @winder in https://github.com/algorand/java-algorand-sdk/pull/360 +### Enhancements +* Enhancement: Use Sandbox for Testing by @tzaffi in https://github.com/algorand/java-algorand-sdk/pull/363 +* Enhancement: Deprecating use of langspec by @ahangsu in https://github.com/algorand/java-algorand-sdk/pull/367 + +## New Contributors +* @tzaffi made their first contribution in https://github.com/algorand/java-algorand-sdk/pull/363 + +**Full Changelog**: https://github.com/algorand/java-algorand-sdk/compare/1.17.0...1.18.0-beta-1 + # 1.17.0 ### New Features * DevTools: adding source map decoder by @barnjamin in https://github.com/algorand/java-algorand-sdk/pull/352 diff --git a/Makefile b/Makefile index 9e4d4850a..be60d45d2 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,24 @@ +UNIT_TAGS := "$(subst :, or ,$(shell awk '{print $2}' src/test/unit.tags | paste -s -d: -))" +INTEGRATION_TAGS := "$(subst :, or ,$(shell awk '{print $2}' src/test/integration.tags | paste -s -d: -))" + unit: - mvn test -Dcucumber.filter.tags="@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.indexer.rekey or @unit.transactions or @unit.transactions.keyreg or @unit.responses or @unit.applications or @unit.dryrun or @unit.tealsign or @unit.responses.messagepack or @unit.responses.231 or @unit.responses.messagepack.231 or @unit.feetest or @unit.indexer.logs or @unit.abijson or @unit.abijson.byname or @unit.atomic_transaction_composer or @unit.transactions.payment or @unit.responses.unlimited_assets or @unit.algod.ledger_refactoring or @unit.indexer.ledger_refactoring or @unit.dryrun.trace.application or @unit.sourcemap" + mvn test -Dcucumber.filter.tags=$(UNIT_TAGS) integration: - mvn test \ - -Dtest=com.algorand.algosdk.integration.RunCucumberIntegrationTest \ - -Dcucumber.filter.tags="@algod or @assets or @auction or @kmd or @send or @send.keyregtxn or @indexer or @rekey_v1 or @applications.verified or @applications or @compile or @dryrun or @indexer.applications or @indexer.231 or @abi or @c2c or @compile.sourcemap" + mvn test -Dtest=com.algorand.algosdk.integration.RunCucumberIntegrationTest -Dcucumber.filter.tags=$(INTEGRATION_TAGS) + +display-all-java-steps: + find src/test/java/com/algorand/algosdk -name "*.java" | xargs grep "io.cucumber.java.en" 2>/dev/null | grep -v Binary | cut -d: -f1 | sort | uniq | xargs grep -E "@(Given|Then|When)" + +harness: + ./test-harness.sh + +docker-javasdk-build: + # Build SDK testing environment + docker build -t java-sdk-testing . + +docker-javasdk-run: + # Launch SDK testing + docker run -it --network host java-sdk-testing:latest -docker-test: - ./run_integration_tests.sh +docker-test: harness docker-javasdk-build docker-javasdk-run diff --git a/README.md b/README.md index b57d49bfa..5e5d913e1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Maven: com.algorand algosdk - 1.17.0 + 1.18.0 ``` @@ -219,6 +219,13 @@ There is also a special integration test environment, and shared tests. To run t ~$ make docker-test ``` +To stand up the test harness, without running the entire test suite use the Makefile: +``` +~$ make harness +``` +You can then run specific cucumber-based unit and integration tests directly. + + ## deploying artifacts The generated pom file provides maven compatibility and deploy capabilities. @@ -229,6 +236,14 @@ mvn clean site -P github,default # for javadoc mvn clean deploy -P release,default ``` +# Testing + +Many cross-SDK tests are defined in [algorand-sdk-testing](https://github.com/algorand/algorand-sdk-testing/). Some are integration tests with additional dependencies. These dependencies are containerized in a docker file, which can be executed with `make docker-test`. + +It is occasionally useful to run locally, or against alternate integration branches. To do this: +1. Install feature files for your test branch "./run_integration_tests.sh -feature-only -test-branch " +2. Run locally with `make integration` and `make unit`, or from the IDE by running "RunCucumberUnitTest.java" + # Android Support Significant work has been taken to ensure Android compatibility (in particular for `minSdkVersion` 16). Note that the @@ -245,7 +260,7 @@ A testing framework can also be generated with: `com.algorand.sdkutils.RunQueryM ## Regenerate the Client Code -To actually regenerate the code, use `run_generator.sh` with paths to the `*.oas2.json` files mentioned above. +The actual generation is done using the `generate_java.sh` script in the [generator](https://github.com/algorand/generator/) repo. # Updating the `kmd` REST client The `kmd` REST client has not been upgraded to use the new code generation, it is still largely autogenerated by `swagger-codegen`. [https://github.com/swagger-api/swagger-codegen] diff --git a/pom.xml b/pom.xml index f2a183f17..4ef3c145b 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.algorand algosdk - 1.17.0 + 1.18.0 jar ${project.groupId}:${project.artifactId} diff --git a/run_integration_tests.sh b/run_integration_tests.sh deleted file mode 100755 index 0fcdccce7..000000000 --- a/run_integration_tests.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootdir=`dirname $0` -pushd $rootdir - -SKIP_TEST_CONTAINER=0 -UPDATE_FEATURE_FILES_ONLY=0 -TEST_BRANCH=master - -function help { - echo "Options:" - echo " -local skip launching the test container." - echo " -feature-only don't bring up test environment or launch test container." - echo " -test-branch install feature files from this branch instead of the default branch." -} - -function my_exit { - popd - exit $1 -} - -while [ "$1" != "" ]; do - case "$1" in - -local) - SKIP_TEST_CONTAINER=1 - ;; - -feature-only) - UPDATE_FEATURE_FILES_ONLY=1 - ;; - -test-branch) - shift - TEST_BRANCH=$1 - ;; - *) - echo "Unknown option $1" - help - my_exit 0 - ;; - esac - shift -done - -# Reset test harness -rm -rf test-harness -git clone --single-branch --branch ${TEST_BRANCH} https://github.com/algorand/algorand-sdk-testing.git test-harness - -## Copy feature files into the project resources - -rm -rf src/test/resources/com/algorand/algosdk/integration -rm -rf src/test/resources/com/algorand/algosdk/unit -mkdir -p src/test/resources/com/algorand/algosdk/integration -mkdir -p src/test/resources/com/algorand/algosdk/unit - -# The Java implementation of these is too tightly coupled with the -# integration tests, so add them to the integration tests instead. -mv test-harness/features/unit/offline.feature test-harness/features/integration/ - -cp -r test-harness/features/integration/* src/test/resources/com/algorand/algosdk/integration -cp -r test-harness/features/unit/* src/test/resources/com/algorand/algosdk/unit -cp -r test-harness/features/resources/* src/test/resources/ - -if [ "${UPDATE_FEATURE_FILES_ONLY}" == "1" ]; then - my_exit 1 -fi - -# Start test harness environment -./test-harness/scripts/up.sh - -if [ "${SKIP_TEST_CONTAINER}" == "1" ]; then - my_exit 1 -fi - -# Build SDK testing environment -docker build -t java-sdk-testing -f Dockerfile "$(pwd)" - -# Launch SDK testing -docker run -it \ - --network host \ - java-sdk-testing:latest - -my_exit 0 diff --git a/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java b/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java index 91c74de45..7a3b92ffc 100644 --- a/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java +++ b/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.codec.binary.Base64; /** * Serializable logicsig class. @@ -39,6 +40,59 @@ public class LogicsigSignature { @JsonProperty("msig") public MultisigSignature msig; + + private static boolean isAsciiPrintable(final byte symbol) { + // linebreak existence check in program byte + boolean isBreakLine = symbol == '\n'; + // printable ascii between range 32 (space) and 126 (tilde ~) + boolean isStdPrintable = symbol >= ' ' && symbol <= '~'; + return isBreakLine || isStdPrintable; + } + + private static boolean isAsciiPrintable(final byte[] program) { + for (byte b : program) { + if (!isAsciiPrintable(b)) + return false; + } + return true; + } + + /** + * Performs heuristic program validation: + * check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes + * @param program + */ + private static void sanityCheckProgram(final byte[] program) { + if (program == null || program.length == 0) + throw new IllegalArgumentException("empty program"); + // in any case, if a slice of "program-bytes" is full of ASCII printable, + // then the slice of bytes can't be Teal program bytes. + // need to check what possible kind of bytes are passed in. + if (isAsciiPrintable(program)) { + // maybe the bytes passed in are representing an Algorand address + boolean isAddress = false; + try { + new Address(new String(program)); + isAddress = true; + } catch (NoSuchAlgorithmException | IllegalArgumentException e) { + // if exception is IllegalArgException, it means bytes are not Algorand address + if (e instanceof NoSuchAlgorithmException) + throw new IllegalArgumentException("cannot check if program bytes are Algorand address" + e); + } + if (isAddress) + throw new IllegalArgumentException("requesting program bytes, get Algorand address"); + + // or maybe these bytes are some B64 encoded bytes representation + if (Base64.isBase64(program)) + throw new IllegalArgumentException("program should not be b64 encoded"); + + // can't further analyze, but it is more than just B64 encoding at this point + throw new IllegalArgumentException( + "program bytes are all ASCII printable characters, not looking like Teal byte code" + ); + } + } + @JsonCreator public LogicsigSignature( @JsonProperty("l") byte[] logic, @@ -48,14 +102,8 @@ public LogicsigSignature( ) { this.logic = Objects.requireNonNull(logic, "program must not be null"); this.args = args; - boolean verified = false; - try { - verified = Logic.checkProgram(this.logic, this.args); - } catch (IOException ex) { - throw new IllegalArgumentException("invalid program", ex); - } - assert verified; + sanityCheckProgram(this.logic); if (sig != null) this.sig = new Signature(sig); this.msig = msig; @@ -124,11 +172,7 @@ public boolean verify(Address singleSigner) throws NoSuchAlgorithmException { return false; } - try { - Logic.checkProgram(this.logic, this.args); - } catch (Exception ex) { - return false; - } + sanityCheckProgram(this.logic); PublicKey pk; try { diff --git a/src/main/java/com/algorand/algosdk/logic/Logic.java b/src/main/java/com/algorand/algosdk/logic/Logic.java index ef4202e08..42965e295 100644 --- a/src/main/java/com/algorand/algosdk/logic/Logic.java +++ b/src/main/java/com/algorand/algosdk/logic/Logic.java @@ -14,7 +14,11 @@ /** * Logic class provides static checkProgram function * that can be used for client-side program validation for size and execution cost. + * + * @deprecated this class is deprecated for relying on metadata (`langspec.json`) that + * does not accurately represent opcode behavior across program versions. */ +@Deprecated public class Logic { private static final int MAX_COST = 20000; @@ -103,7 +107,7 @@ protected static class ByteConstBlock { * Each byte in a varint, except the last byte, has the most significant * bit (msb) set – this indicates that there are further bytes to come. * The lower 7 bits of each byte are used to store the two's complement - * representation of the number in groups of 7 bits, least significant + * representation of the number in groups of 7 bits, the least significant * group first. * https://developers.google.com/protocol-buffers/docs/encoding * @param value being serialized @@ -128,7 +132,7 @@ public static byte[] putUVarint(int value) { * Given a varint, get the integer value * @param buffer serialized varint * @param bufferOffset position in the buffer to start reading from - * @return pair of values in in array: value, read size + * @return pair of values in an array: value, read size */ public static VarintResult getUVarint(byte [] buffer, int bufferOffset) { int x = 0; @@ -164,6 +168,7 @@ public static boolean checkProgram(byte[] program, List args) throws IOE /** * Performs basic program validation: instruction count and program cost + * * @param program Program to validate * @param args Program arguments to validate * @return boolean diff --git a/src/main/java/com/algorand/algosdk/transaction/Transaction.java b/src/main/java/com/algorand/algosdk/transaction/Transaction.java index 98963413d..0f3531c07 100644 --- a/src/main/java/com/algorand/algosdk/transaction/Transaction.java +++ b/src/main/java/com/algorand/algosdk/transaction/Transaction.java @@ -162,6 +162,16 @@ public class Transaction implements Serializable { @JsonProperty("apep") public Long extraPages = 0L; + /* state proof fields */ + @JsonProperty("sptype") + public Integer stateProofType = null; + + @JsonProperty("sp") + public Map stateProof = null; + + @JsonProperty("spmsg") + public Map stateProofMessage = null; + /** * Create a payment transaction * @param fromAddr source address @@ -722,7 +732,7 @@ private Transaction(@JsonProperty("type") Type type, } /** - * Constructor which takes all the fields of Transaction except for nonpart and state proof. + * Constructor which takes all the fields of Transaction except for nonpart. * For details about which fields to use with different transaction types, refer to the developer documentation: * https://developer.algorand.org/docs/reference/transactions/#asset-transfer-transaction */ @@ -1250,7 +1260,8 @@ public enum Type { AssetConfig("acfg"), AssetTransfer("axfer"), AssetFreeze("afrz"), - ApplicationCall("appl"); + ApplicationCall("appl"), + StateProof("stpf"); private static Map namesMap = new HashMap(6); diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetLightBlockHeaderProof.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLightBlockHeaderProof.java new file mode 100644 index 000000000..ecf9cdf87 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLightBlockHeaderProof.java @@ -0,0 +1,66 @@ +package com.algorand.algosdk.v2.client.algod; + +import com.algorand.algosdk.v2.client.common.Client; +import com.algorand.algosdk.v2.client.common.HttpMethod; +import com.algorand.algosdk.v2.client.common.Query; +import com.algorand.algosdk.v2.client.common.QueryData; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.LightBlockHeaderProof; + + +/** + * Gets a proof for a given light block header inside a state proof commitment + * /v2/blocks/{round}/lightheader/proof + */ +public class GetLightBlockHeaderProof extends Query { + + private Long round; + + /** + * @param round The round to which the light block header belongs. + */ + public GetLightBlockHeaderProof(Client client, Long round) { + super(client, new HttpMethod("get")); + this.round = round; + } + + /** + * Execute the query. + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(LightBlockHeaderProof.class); + return resp; + } + + /** + * Execute the query with custom headers, there must be an equal number of keys and values + * or else an error will be generated. + * @param headers an array of header keys + * @param values an array of header values + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute(String[] headers, String[] values) throws Exception { + Response resp = baseExecute(headers, values); + resp.setValueType(LightBlockHeaderProof.class); + return resp; + } + + protected QueryData getRequestString() { + if (this.round == null) { + throw new RuntimeException("round is not set. It is a required parameter."); + } + addPathSegment(String.valueOf("v2")); + addPathSegment(String.valueOf("blocks")); + addPathSegment(String.valueOf(round)); + addPathSegment(String.valueOf("lightheader")); + addPathSegment(String.valueOf("proof")); + + return qd; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetStateProof.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetStateProof.java new file mode 100644 index 000000000..dc35ba3cd --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetStateProof.java @@ -0,0 +1,64 @@ +package com.algorand.algosdk.v2.client.algod; + +import com.algorand.algosdk.v2.client.common.Client; +import com.algorand.algosdk.v2.client.common.HttpMethod; +import com.algorand.algosdk.v2.client.common.Query; +import com.algorand.algosdk.v2.client.common.QueryData; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.StateProof; + + +/** + * Get a state proof that covers a given round + * /v2/stateproofs/{round} + */ +public class GetStateProof extends Query { + + private Long round; + + /** + * @param round The round for which a state proof is desired. + */ + public GetStateProof(Client client, Long round) { + super(client, new HttpMethod("get")); + this.round = round; + } + + /** + * Execute the query. + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(StateProof.class); + return resp; + } + + /** + * Execute the query with custom headers, there must be an equal number of keys and values + * or else an error will be generated. + * @param headers an array of header keys + * @param values an array of header values + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute(String[] headers, String[] values) throws Exception { + Response resp = baseExecute(headers, values); + resp.setValueType(StateProof.class); + return resp; + } + + protected QueryData getRequestString() { + if (this.round == null) { + throw new RuntimeException("round is not set. It is a required parameter."); + } + addPathSegment(String.valueOf("v2")); + addPathSegment(String.valueOf("stateproofs")); + addPathSegment(String.valueOf(round)); + + return qd; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetProof.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionProof.java similarity index 73% rename from src/main/java/com/algorand/algosdk/v2/client/algod/GetProof.java rename to src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionProof.java index 6824d0f4c..edde98b7e 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/algod/GetProof.java +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionProof.java @@ -6,14 +6,14 @@ import com.algorand.algosdk.v2.client.common.QueryData; import com.algorand.algosdk.v2.client.common.Response; import com.algorand.algosdk.v2.client.model.Enums; -import com.algorand.algosdk.v2.client.model.ProofResponse; +import com.algorand.algosdk.v2.client.model.TransactionProofResponse; /** - * Get a Merkle proof for a transaction in a block. + * Get a proof for a transaction in a block. * /v2/blocks/{round}/transactions/{txid}/proof */ -public class GetProof extends Query { +public class GetTransactionProof extends Query { private Long round; private String txid; @@ -22,7 +22,7 @@ public class GetProof extends Query { * @param round The round in which the transaction appears. * @param txid The transaction ID for which to generate a proof. */ - public GetProof(Client client, Long round, String txid) { + public GetTransactionProof(Client client, Long round, String txid) { super(client, new HttpMethod("get")); addQuery("format", "msgpack"); this.round = round; @@ -34,7 +34,7 @@ public GetProof(Client client, Long round, String txid) { * sha512_256 * sha256 */ - public GetProof hashtype(Enums.Hashtype hashtype) { + public GetTransactionProof hashtype(Enums.Hashtype hashtype) { addQuery("hashtype", String.valueOf(hashtype)); return this; } @@ -45,9 +45,9 @@ public GetProof hashtype(Enums.Hashtype hashtype) { * @throws Exception */ @Override - public Response execute() throws Exception { - Response resp = baseExecute(); - resp.setValueType(ProofResponse.class); + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(TransactionProofResponse.class); return resp; } @@ -60,9 +60,9 @@ public Response execute() throws Exception { * @throws Exception */ @Override - public Response execute(String[] headers, String[] values) throws Exception { - Response resp = baseExecute(headers, values); - resp.setValueType(ProofResponse.class); + public Response execute(String[] headers, String[] values) throws Exception { + Response resp = baseExecute(headers, values); + resp.setValueType(TransactionProofResponse.class); return resp; } diff --git a/src/main/java/com/algorand/algosdk/v2/client/common/AlgodClient.java b/src/main/java/com/algorand/algosdk/v2/client/common/AlgodClient.java index 4d2909782..920996c26 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/common/AlgodClient.java +++ b/src/main/java/com/algorand/algosdk/v2/client/common/AlgodClient.java @@ -10,7 +10,7 @@ import com.algorand.algosdk.v2.client.algod.AccountApplicationInformation; import com.algorand.algosdk.v2.client.algod.GetPendingTransactionsByAddress; import com.algorand.algosdk.v2.client.algod.GetBlock; -import com.algorand.algosdk.v2.client.algod.GetProof; +import com.algorand.algosdk.v2.client.algod.GetTransactionProof; import com.algorand.algosdk.v2.client.algod.GetSupply; import com.algorand.algosdk.v2.client.algod.GetStatus; import com.algorand.algosdk.v2.client.algod.WaitForBlock; @@ -18,6 +18,8 @@ import com.algorand.algosdk.v2.client.algod.TransactionParams; import com.algorand.algosdk.v2.client.algod.GetPendingTransactions; import com.algorand.algosdk.v2.client.algod.PendingTransactionInformation; +import com.algorand.algosdk.v2.client.algod.GetStateProof; +import com.algorand.algosdk.v2.client.algod.GetLightBlockHeaderProof; import com.algorand.algosdk.v2.client.algod.GetApplicationByID; import com.algorand.algosdk.v2.client.algod.GetAssetByID; import com.algorand.algosdk.v2.client.algod.TealCompile; @@ -140,12 +142,12 @@ public GetBlock GetBlock(Long round) { } /** - * Get a Merkle proof for a transaction in a block. + * Get a proof for a transaction in a block. * /v2/blocks/{round}/transactions/{txid}/proof */ - public GetProof GetProof(Long round, + public GetTransactionProof GetTransactionProof(Long round, String txid) { - return new GetProof((Client) this, round, txid); + return new GetTransactionProof((Client) this, round, txid); } /** @@ -213,6 +215,22 @@ public PendingTransactionInformation PendingTransactionInformation(String txid) return new PendingTransactionInformation((Client) this, txid); } + /** + * Get a state proof that covers a given round + * /v2/stateproofs/{round} + */ + public GetStateProof GetStateProof(Long round) { + return new GetStateProof((Client) this, round); + } + + /** + * Gets a proof for a given light block header inside a state proof commitment + * /v2/blocks/{round}/lightheader/proof + */ + public GetLightBlockHeaderProof GetLightBlockHeaderProof(Long round) { + return new GetLightBlockHeaderProof((Client) this, round); + } + /** * Given a application ID, it returns application information including creator, * approval and clear programs, global and local schemas, and global state. diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/Block.java b/src/main/java/com/algorand/algosdk/v2/client/model/Block.java index 442ab604c..83078c5ee 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/Block.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/Block.java @@ -69,6 +69,12 @@ public String seed() { } public byte[] seed; + /** + * Tracks the status of state proofs. + */ + @JsonProperty("state-proof-tracking") + public List stateProofTracking = new ArrayList(); + /** * (ts) Block creation timestamp in seconds since eposh */ @@ -98,6 +104,21 @@ public String transactionsRoot() { } public byte[] transactionsRoot; + /** + * (txn256) TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA256 hash function instead of + * the default SHA512_256. This commitment can be used on environments where only + * the SHA256 function exists. + */ + @JsonProperty("transactions-root-sha256") + public void transactionsRootSha256(String base64Encoded) { + this.transactionsRootSha256 = Encoder.decodeFromBase64(base64Encoded); + } + public String transactionsRootSha256() { + return Encoder.encodeToBase64(this.transactionsRootSha256); + } + public byte[] transactionsRootSha256; + /** * (tc) TxnCounter counts the number of transactions committed in the ledger, from * the time at which support for this feature was introduced. @@ -133,9 +154,11 @@ public boolean equals(Object o) { if (!Objects.deepEquals(this.rewards, other.rewards)) return false; if (!Objects.deepEquals(this.round, other.round)) return false; if (!Objects.deepEquals(this.seed, other.seed)) return false; + if (!Objects.deepEquals(this.stateProofTracking, other.stateProofTracking)) return false; if (!Objects.deepEquals(this.timestamp, other.timestamp)) return false; if (!Objects.deepEquals(this.transactions, other.transactions)) return false; if (!Objects.deepEquals(this.transactionsRoot, other.transactionsRoot)) return false; + if (!Objects.deepEquals(this.transactionsRootSha256, other.transactionsRootSha256)) return false; if (!Objects.deepEquals(this.txnCounter, other.txnCounter)) return false; if (!Objects.deepEquals(this.upgradeState, other.upgradeState)) return false; if (!Objects.deepEquals(this.upgradeVote, other.upgradeVote)) return false; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/Enums.java b/src/main/java/com/algorand/algosdk/v2/client/model/Enums.java index 8fc2fec9e..df6b7c775 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/Enums.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/Enums.java @@ -191,6 +191,7 @@ public static SigType forValue(String value) { * (axfer) asset-transfer-transaction * (afrz) asset-freeze-transaction * (appl) application-transaction + * (stpf) state-proof-transaction */ public enum TxType { @JsonProperty("pay") PAY("pay"), @@ -199,6 +200,7 @@ public enum TxType { @JsonProperty("axfer") AXFER("axfer"), @JsonProperty("afrz") AFRZ("afrz"), @JsonProperty("appl") APPL("appl"), + @JsonProperty("stpf") STPF("stpf"), @JsonProperty("") UNKNOWN(""); final String serializedName; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/HashFactory.java b/src/main/java/com/algorand/algosdk/v2/client/model/HashFactory.java new file mode 100644 index 000000000..56e436772 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/HashFactory.java @@ -0,0 +1,27 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class HashFactory extends PathResponse { + + /** + * (t) + */ + @JsonProperty("hash-type") + public Long hashType; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + HashFactory other = (HashFactory) o; + if (!Objects.deepEquals(this.hashType, other.hashType)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/IndexerStateProofMessage.java b/src/main/java/com/algorand/algosdk/v2/client/model/IndexerStateProofMessage.java new file mode 100644 index 000000000..9358b4168 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/IndexerStateProofMessage.java @@ -0,0 +1,68 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class IndexerStateProofMessage extends PathResponse { + + /** + * (b) + */ + @JsonProperty("block-headers-commitment") + public void blockHeadersCommitment(String base64Encoded) { + this.blockHeadersCommitment = Encoder.decodeFromBase64(base64Encoded); + } + public String blockHeadersCommitment() { + return Encoder.encodeToBase64(this.blockHeadersCommitment); + } + public byte[] blockHeadersCommitment; + + /** + * (f) + */ + @JsonProperty("first-attested-round") + public java.math.BigInteger firstAttestedRound; + + /** + * (l) + */ + @JsonProperty("latest-attested-round") + public java.math.BigInteger latestAttestedRound; + + /** + * (P) + */ + @JsonProperty("ln-proven-weight") + public java.math.BigInteger lnProvenWeight; + + /** + * (v) + */ + @JsonProperty("voters-commitment") + public void votersCommitment(String base64Encoded) { + this.votersCommitment = Encoder.decodeFromBase64(base64Encoded); + } + public String votersCommitment() { + return Encoder.encodeToBase64(this.votersCommitment); + } + public byte[] votersCommitment; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + IndexerStateProofMessage other = (IndexerStateProofMessage) o; + if (!Objects.deepEquals(this.blockHeadersCommitment, other.blockHeadersCommitment)) return false; + if (!Objects.deepEquals(this.firstAttestedRound, other.firstAttestedRound)) return false; + if (!Objects.deepEquals(this.latestAttestedRound, other.latestAttestedRound)) return false; + if (!Objects.deepEquals(this.lnProvenWeight, other.lnProvenWeight)) return false; + if (!Objects.deepEquals(this.votersCommitment, other.votersCommitment)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/LightBlockHeaderProof.java b/src/main/java/com/algorand/algosdk/v2/client/model/LightBlockHeaderProof.java new file mode 100644 index 000000000..f99af414f --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/LightBlockHeaderProof.java @@ -0,0 +1,52 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Proof of membership and position of a light block header. + */ +public class LightBlockHeaderProof extends PathResponse { + + /** + * The index of the light block header in the vector commitment tree + */ + @JsonProperty("index") + public Long index; + + /** + * The encoded proof. + */ + @JsonProperty("proof") + public void proof(String base64Encoded) { + this.proof = Encoder.decodeFromBase64(base64Encoded); + } + public String proof() { + return Encoder.encodeToBase64(this.proof); + } + public byte[] proof; + + /** + * Represents the depth of the tree that is being proven, i.e. the number of edges + * from a leaf to the root. + */ + @JsonProperty("treedepth") + public Long treedepth; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + LightBlockHeaderProof other = (LightBlockHeaderProof) o; + if (!Objects.deepEquals(this.index, other.index)) return false; + if (!Objects.deepEquals(this.proof, other.proof)) return false; + if (!Objects.deepEquals(this.treedepth, other.treedepth)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/MerkleArrayProof.java b/src/main/java/com/algorand/algosdk/v2/client/model/MerkleArrayProof.java new file mode 100644 index 000000000..454d526d8 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/MerkleArrayProof.java @@ -0,0 +1,57 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MerkleArrayProof extends PathResponse { + + @JsonProperty("hash-factory") + public HashFactory hashFactory; + + /** + * (pth) + */ + @JsonProperty("path") + public List path = new ArrayList(); + @JsonIgnore + public void path(List base64Encoded) { + this.path = new ArrayList(); + for (String val : base64Encoded) { + this.path.add(Encoder.decodeFromBase64(val)); + } + } + @JsonIgnore + public List path() { + ArrayList ret = new ArrayList(); + for (byte[] val : this.path) { + ret.add(Encoder.encodeToBase64(val)); + } + return ret; + } + + /** + * (td) + */ + @JsonProperty("tree-depth") + public Long treeDepth; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + MerkleArrayProof other = (MerkleArrayProof) o; + if (!Objects.deepEquals(this.hashFactory, other.hashFactory)) return false; + if (!Objects.deepEquals(this.path, other.path)) return false; + if (!Objects.deepEquals(this.treeDepth, other.treeDepth)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProof.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProof.java new file mode 100644 index 000000000..7fd5a2301 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProof.java @@ -0,0 +1,44 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a state proof and its corresponding message + */ +public class StateProof extends PathResponse { + + /** + * Represents the message that the state proofs are attesting to. + */ + @JsonProperty("Message") + public StateProofMessage message; + + /** + * The encoded StateProof for the message. + */ + @JsonProperty("StateProof") + public void stateProof(String base64Encoded) { + this.stateProof = Encoder.decodeFromBase64(base64Encoded); + } + public String stateProof() { + return Encoder.encodeToBase64(this.stateProof); + } + public byte[] stateProof; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProof other = (StateProof) o; + if (!Objects.deepEquals(this.message, other.message)) return false; + if (!Objects.deepEquals(this.stateProof, other.stateProof)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofFields.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofFields.java new file mode 100644 index 000000000..049ea5a9b --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofFields.java @@ -0,0 +1,84 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ +public class StateProofFields extends PathResponse { + + /** + * (P) + */ + @JsonProperty("part-proofs") + public MerkleArrayProof partProofs; + + /** + * (pr) Sequence of reveal positions. + */ + @JsonProperty("positions-to-reveal") + public List positionsToReveal = new ArrayList(); + + /** + * (r) Note that this is actually stored as a map[uint64] - Reveal in the actual + * msgp + */ + @JsonProperty("reveals") + public List reveals = new ArrayList(); + + /** + * (v) Salt version of the merkle signature. + */ + @JsonProperty("salt-version") + public Long saltVersion; + + /** + * (c) + */ + @JsonProperty("sig-commit") + public void sigCommit(String base64Encoded) { + this.sigCommit = Encoder.decodeFromBase64(base64Encoded); + } + public String sigCommit() { + return Encoder.encodeToBase64(this.sigCommit); + } + public byte[] sigCommit; + + /** + * (S) + */ + @JsonProperty("sig-proofs") + public MerkleArrayProof sigProofs; + + /** + * (w) + */ + @JsonProperty("signed-weight") + public java.math.BigInteger signedWeight; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofFields other = (StateProofFields) o; + if (!Objects.deepEquals(this.partProofs, other.partProofs)) return false; + if (!Objects.deepEquals(this.positionsToReveal, other.positionsToReveal)) return false; + if (!Objects.deepEquals(this.reveals, other.reveals)) return false; + if (!Objects.deepEquals(this.saltVersion, other.saltVersion)) return false; + if (!Objects.deepEquals(this.sigCommit, other.sigCommit)) return false; + if (!Objects.deepEquals(this.sigProofs, other.sigProofs)) return false; + if (!Objects.deepEquals(this.signedWeight, other.signedWeight)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofMessage.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofMessage.java new file mode 100644 index 000000000..1ee30534a --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofMessage.java @@ -0,0 +1,73 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the message that the state proofs are attesting to. + */ +public class StateProofMessage extends PathResponse { + + /** + * The vector commitment root on all light block headers within a state proof + * interval. + */ + @JsonProperty("BlockHeadersCommitment") + public void blockHeadersCommitment(String base64Encoded) { + this.blockHeadersCommitment = Encoder.decodeFromBase64(base64Encoded); + } + public String blockHeadersCommitment() { + return Encoder.encodeToBase64(this.blockHeadersCommitment); + } + public byte[] blockHeadersCommitment; + + /** + * The first round the message attests to. + */ + @JsonProperty("FirstAttestedRound") + public java.math.BigInteger firstAttestedRound; + + /** + * The last round the message attests to. + */ + @JsonProperty("LastAttestedRound") + public java.math.BigInteger lastAttestedRound; + + /** + * An integer value representing the natural log of the proven weight with 16 bits + * of precision. This value would be used to verify the next state proof. + */ + @JsonProperty("LnProvenWeight") + public java.math.BigInteger lnProvenWeight; + + /** + * The vector commitment root of the top N accounts to sign the next StateProof. + */ + @JsonProperty("VotersCommitment") + public void votersCommitment(String base64Encoded) { + this.votersCommitment = Encoder.decodeFromBase64(base64Encoded); + } + public String votersCommitment() { + return Encoder.encodeToBase64(this.votersCommitment); + } + public byte[] votersCommitment; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofMessage other = (StateProofMessage) o; + if (!Objects.deepEquals(this.blockHeadersCommitment, other.blockHeadersCommitment)) return false; + if (!Objects.deepEquals(this.firstAttestedRound, other.firstAttestedRound)) return false; + if (!Objects.deepEquals(this.lastAttestedRound, other.lastAttestedRound)) return false; + if (!Objects.deepEquals(this.lnProvenWeight, other.lnProvenWeight)) return false; + if (!Objects.deepEquals(this.votersCommitment, other.votersCommitment)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofParticipant.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofParticipant.java new file mode 100644 index 000000000..e48d99581 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofParticipant.java @@ -0,0 +1,34 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofParticipant extends PathResponse { + + /** + * (p) + */ + @JsonProperty("verifier") + public StateProofVerifier verifier; + + /** + * (w) + */ + @JsonProperty("weight") + public java.math.BigInteger weight; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofParticipant other = (StateProofParticipant) o; + if (!Objects.deepEquals(this.verifier, other.verifier)) return false; + if (!Objects.deepEquals(this.weight, other.weight)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofReveal.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofReveal.java new file mode 100644 index 000000000..4db51d66c --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofReveal.java @@ -0,0 +1,42 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofReveal extends PathResponse { + + /** + * (p) + */ + @JsonProperty("participant") + public StateProofParticipant participant; + + /** + * The position in the signature and participants arrays corresponding to this + * entry. + */ + @JsonProperty("position") + public java.math.BigInteger position; + + /** + * (s) + */ + @JsonProperty("sig-slot") + public StateProofSigSlot sigSlot; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofReveal other = (StateProofReveal) o; + if (!Objects.deepEquals(this.participant, other.participant)) return false; + if (!Objects.deepEquals(this.position, other.position)) return false; + if (!Objects.deepEquals(this.sigSlot, other.sigSlot)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSigSlot.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSigSlot.java new file mode 100644 index 000000000..7f9a7cd69 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSigSlot.java @@ -0,0 +1,31 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofSigSlot extends PathResponse { + + /** + * (l) The total weight of signatures in the lower-numbered slots. + */ + @JsonProperty("lower-sig-weight") + public java.math.BigInteger lowerSigWeight; + + @JsonProperty("signature") + public StateProofSignature signature; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofSigSlot other = (StateProofSigSlot) o; + if (!Objects.deepEquals(this.lowerSigWeight, other.lowerSigWeight)) return false; + if (!Objects.deepEquals(this.signature, other.signature)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSignature.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSignature.java new file mode 100644 index 000000000..4491515f9 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofSignature.java @@ -0,0 +1,52 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofSignature extends PathResponse { + + @JsonProperty("falcon-signature") + public void falconSignature(String base64Encoded) { + this.falconSignature = Encoder.decodeFromBase64(base64Encoded); + } + public String falconSignature() { + return Encoder.encodeToBase64(this.falconSignature); + } + public byte[] falconSignature; + + @JsonProperty("merkle-array-index") + public Long merkleArrayIndex; + + @JsonProperty("proof") + public MerkleArrayProof proof; + + /** + * (vkey) + */ + @JsonProperty("verifying-key") + public void verifyingKey(String base64Encoded) { + this.verifyingKey = Encoder.decodeFromBase64(base64Encoded); + } + public String verifyingKey() { + return Encoder.encodeToBase64(this.verifyingKey); + } + public byte[] verifyingKey; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofSignature other = (StateProofSignature) o; + if (!Objects.deepEquals(this.falconSignature, other.falconSignature)) return false; + if (!Objects.deepEquals(this.merkleArrayIndex, other.merkleArrayIndex)) return false; + if (!Objects.deepEquals(this.proof, other.proof)) return false; + if (!Objects.deepEquals(this.verifyingKey, other.verifyingKey)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofTracking.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofTracking.java new file mode 100644 index 000000000..aac966d25 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofTracking.java @@ -0,0 +1,57 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofTracking extends PathResponse { + + /** + * (n) Next round for which we will accept a state proof transaction. + */ + @JsonProperty("next-round") + public Long nextRound; + + /** + * (t) The total number of microalgos held by the online accounts during the + * StateProof round. + */ + @JsonProperty("online-total-weight") + public Long onlineTotalWeight; + + /** + * State Proof Type. Note the raw object uses map with this as key. + */ + @JsonProperty("type") + public java.math.BigInteger type; + + /** + * (v) Root of a vector commitment containing online accounts that will help sign + * the proof. + */ + @JsonProperty("voters-commitment") + public void votersCommitment(String base64Encoded) { + this.votersCommitment = Encoder.decodeFromBase64(base64Encoded); + } + public String votersCommitment() { + return Encoder.encodeToBase64(this.votersCommitment); + } + public byte[] votersCommitment; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofTracking other = (StateProofTracking) o; + if (!Objects.deepEquals(this.nextRound, other.nextRound)) return false; + if (!Objects.deepEquals(this.onlineTotalWeight, other.onlineTotalWeight)) return false; + if (!Objects.deepEquals(this.type, other.type)) return false; + if (!Objects.deepEquals(this.votersCommitment, other.votersCommitment)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/StateProofVerifier.java b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofVerifier.java new file mode 100644 index 000000000..4806a63d6 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/StateProofVerifier.java @@ -0,0 +1,41 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class StateProofVerifier extends PathResponse { + + /** + * (cmt) Represents the root of the vector commitment tree. + */ + @JsonProperty("commitment") + public void commitment(String base64Encoded) { + this.commitment = Encoder.decodeFromBase64(base64Encoded); + } + public String commitment() { + return Encoder.encodeToBase64(this.commitment); + } + public byte[] commitment; + + /** + * (lf) Key lifetime. + */ + @JsonProperty("key-lifetime") + public java.math.BigInteger keyLifetime; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + StateProofVerifier other = (StateProofVerifier) o; + if (!Objects.deepEquals(this.commitment, other.commitment)) return false; + if (!Objects.deepEquals(this.keyLifetime, other.keyLifetime)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/Transaction.java b/src/main/java/com/algorand/algosdk/v2/client/model/Transaction.java index 345e6b61f..1ac8a4423 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/Transaction.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/Transaction.java @@ -302,6 +302,14 @@ public String rekeyTo() throws NoSuchAlgorithmException { @JsonProperty("signature") public TransactionSignature signature; + /** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ + @JsonProperty("state-proof-transaction") + public TransactionStateProof stateProofTransaction; + /** * (type) Indicates what type of transaction this is. Different types have * different fields. @@ -312,6 +320,7 @@ public String rekeyTo() throws NoSuchAlgorithmException { * (axfer) asset-transfer-transaction * (afrz) asset-freeze-transaction * (appl) application-transaction + * (stpf) state-proof-transaction */ @JsonProperty("tx-type") public Enums.TxType txType; @@ -355,6 +364,7 @@ public boolean equals(Object o) { if (!Objects.deepEquals(this.sender, other.sender)) return false; if (!Objects.deepEquals(this.senderRewards, other.senderRewards)) return false; if (!Objects.deepEquals(this.signature, other.signature)) return false; + if (!Objects.deepEquals(this.stateProofTransaction, other.stateProofTransaction)) return false; if (!Objects.deepEquals(this.txType, other.txType)) return false; return true; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/ProofResponse.java b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionProofResponse.java similarity index 91% rename from src/main/java/com/algorand/algosdk/v2/client/model/ProofResponse.java rename to src/main/java/com/algorand/algosdk/v2/client/model/TransactionProofResponse.java index 6318e77a3..e74827b8e 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/ProofResponse.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionProofResponse.java @@ -9,7 +9,7 @@ /** * Proof of transaction in a block. */ -public class ProofResponse extends PathResponse { +public class TransactionProofResponse extends PathResponse { /** * The type of hash function used to create the proof, must be one of: @@ -26,7 +26,7 @@ public class ProofResponse extends PathResponse { public Long idx; /** - * Merkle proof of transaction membership. + * Proof of transaction membership. */ @JsonProperty("proof") public void proof(String base64Encoded) { @@ -62,7 +62,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; - ProofResponse other = (ProofResponse) o; + TransactionProofResponse other = (TransactionProofResponse) o; if (!Objects.deepEquals(this.hashtype, other.hashtype)) return false; if (!Objects.deepEquals(this.idx, other.idx)) return false; if (!Objects.deepEquals(this.proof, other.proof)) return false; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/TransactionStateProof.java b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionStateProof.java new file mode 100644 index 000000000..7ffa0868d --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionStateProof.java @@ -0,0 +1,49 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ +public class TransactionStateProof extends PathResponse { + + /** + * (spmsg) + */ + @JsonProperty("message") + public IndexerStateProofMessage message; + + /** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ + @JsonProperty("state-proof") + public StateProofFields stateProof; + + /** + * (sptype) Type of the state proof. Integer representing an entry defined in + * protocol/stateproof.go + */ + @JsonProperty("state-proof-type") + public java.math.BigInteger stateProofType; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + TransactionStateProof other = (TransactionStateProof) o; + if (!Objects.deepEquals(this.message, other.message)) return false; + if (!Objects.deepEquals(this.stateProof, other.stateProof)) return false; + if (!Objects.deepEquals(this.stateProofType, other.stateProofType)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/VersionBuild.java b/src/main/java/com/algorand/algosdk/v2/client/model/VersionBuild.java deleted file mode 100644 index 38dd12457..000000000 --- a/src/main/java/com/algorand/algosdk/v2/client/model/VersionBuild.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.algorand.algosdk.v2.client.model; - -import java.util.Objects; - -import com.algorand.algosdk.util.Encoder; -import com.algorand.algosdk.v2.client.common.PathResponse; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * the current algod build version information. - */ -public class VersionBuild extends PathResponse { - - @JsonProperty("branch") - public String branch; - - @JsonProperty("build-number") - public Long buildNumber; - - @JsonProperty("channel") - public String channel; - - @JsonProperty("commit-hash") - public void commitHash(String base64Encoded) { - this.commitHash = Encoder.decodeFromBase64(base64Encoded); - } - @JsonProperty("commit-hash") - public String commitHash() { - return Encoder.encodeToBase64(this.commitHash); - } - public byte[] commitHash; - - @JsonProperty("major") - public Long major; - - @JsonProperty("minor") - public Long minor; - - @Override - public boolean equals(Object o) { - - if (this == o) return true; - if (o == null) return false; - - VersionBuild other = (VersionBuild) o; - if (!Objects.deepEquals(this.branch, other.branch)) return false; - if (!Objects.deepEquals(this.buildNumber, other.buildNumber)) return false; - if (!Objects.deepEquals(this.channel, other.channel)) return false; - if (!Objects.deepEquals(this.commitHash, other.commitHash)) return false; - if (!Objects.deepEquals(this.major, other.major)) return false; - if (!Objects.deepEquals(this.minor, other.minor)) return false; - - return true; - } -} diff --git a/src/test/integration.tags b/src/test/integration.tags new file mode 100644 index 000000000..c9421bf47 --- /dev/null +++ b/src/test/integration.tags @@ -0,0 +1,14 @@ +@abi +@algod +@applications +@applications.verified +@assets +@auction +@c2c +@compile +@compile.sourcemap +@dryrun +@kmd +@rekey_v1 +@send +@send.keyregtxn diff --git a/src/test/java/com/algorand/algosdk/crypto/TestLogicsigSignature.java b/src/test/java/com/algorand/algosdk/crypto/TestLogicsigSignature.java index e31721bb3..673fbe82e 100644 --- a/src/test/java/com/algorand/algosdk/crypto/TestLogicsigSignature.java +++ b/src/test/java/com/algorand/algosdk/crypto/TestLogicsigSignature.java @@ -62,16 +62,6 @@ public void testLogicsigCreation() throws Exception { assertThat(lsig).isEqualTo(lsig1); } - @Test - public void testLogicsigInvalidProgramCreation() throws Exception { - byte[] program = { - 0x7F, 0x20, 0x01, 0x01, 0x22 - }; - assertThatThrownBy(() -> new LogicsigSignature(program)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("unsupported version"); - } - @Test public void testLogicsigSignature() throws Exception { byte[] program = { diff --git a/src/test/java/com/algorand/algosdk/integration/Clients.java b/src/test/java/com/algorand/algosdk/integration/Clients.java index 89a94cfff..4f4b61b22 100644 --- a/src/test/java/com/algorand/algosdk/integration/Clients.java +++ b/src/test/java/com/algorand/algosdk/integration/Clients.java @@ -15,9 +15,4 @@ public class Clients { public void an_algod_v2_client_connected_to_port_with_token(String host, Integer port, String token) { v2Client = new AlgodClient(host, port, token); } - - @Given("indexer client {int} at {string} port {int} with token {string}") - public void indexer_client_at_port_with_token(Integer index, String uri, Integer port, String token) { - indexerClients.put(index, new IndexerClient(uri, port, "")); - } } diff --git a/src/test/java/com/algorand/algosdk/integration/EvalDelta.java b/src/test/java/com/algorand/algosdk/integration/EvalDelta.java deleted file mode 100644 index 7bc9f8fd2..000000000 --- a/src/test/java/com/algorand/algosdk/integration/EvalDelta.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.algorand.algosdk.integration; - -import com.algorand.algosdk.crypto.Address; -import com.algorand.algosdk.v2.client.model.*; -import io.cucumber.java.en.Then; -import org.assertj.core.api.Assertions; - -import java.math.BigInteger; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; - -public class EvalDelta { - private final Clients clients; - private final TransientAccount transientAccount; - private final Applications applications; - - public EvalDelta(Clients clients, TransientAccount transientAccount, Applications applications) { - this.clients = clients; - this.transientAccount = transientAccount; - this.applications = applications; - } - - @Then("the unconfirmed pending transaction by ID should have no apply data fields.") - public void the_unconfirmed_pending_transaction_by_ID_should_have_no_apply_data_fields() throws Exception { - PendingTransactionResponse r = clients.v2Client - .PendingTransactionInformation(applications.txId) - .execute() - .body(); - - // Just in case we missed the boat and the tx is now confirmed. - if (r.confirmedRound == null) { - assertThat(r.globalStateDelta).isEmpty(); - assertThat(r.localStateDelta).isEmpty(); - } - } - - private List getAccountDelta(Address addr, List data) { - return data.stream() - .filter(ad -> ad.address.equals(addr)) - .map(ad -> ad.delta) - .findAny() - .orElse(null); - } - - @Then("the confirmed pending transaction by ID should have a {string} state change for {string} to {string}, indexer {int} should also confirm this.") - public void checkConfirmedTransaction(String stateLocation, String key, String newValue, int indexer) throws Exception { - PendingTransactionResponse r = clients.v2Client - .PendingTransactionInformation(applications.txId) - .execute() - .body(); - - // Try fetching the transaction from indexer a few times to give indexer a chance to process the new block. - Transaction tx = null; - for (int i = 0; i < 5 && tx == null; i++) { - TransactionsResponse indexerResponse = clients.indexerClients.get(indexer) - .searchForTransactions() - .txid(applications.txId) - .execute() - .body(); - if (! indexerResponse.transactions.isEmpty()) { - tx = indexerResponse.transactions.get(0); - } else { - Thread.sleep(TimeUnit.SECONDS.toMillis(1)); - } - } - - assertThat(tx).as("Indexer was unable to return txid %s.", applications.txId).isNotNull(); - - // Grab local or global key/value deltas. - // Algod - List keyValuesAlgod = null; - // Indexer - List keyValuesIndexer = null; - switch(stateLocation) { - case "local": - Address localAccountKey = r.txn.tx.sender; - keyValuesAlgod = getAccountDelta(localAccountKey, r.localStateDelta); - keyValuesIndexer = getAccountDelta(localAccountKey, tx.localStateDelta); - break; - case "global": - keyValuesAlgod = r.globalStateDelta; - keyValuesIndexer = tx.globalStateDelta; - break; - default: - Assertions.fail("Unknown state location: %s", stateLocation); - } - - // Algod - assertThat(keyValuesAlgod).hasSize(1); - EvalDeltaKeyValue keyValueAlgod = keyValuesAlgod.get(0); - assertThat(keyValueAlgod.key).isEqualTo(key); - - // Indexer - assertThat(keyValuesIndexer).hasSize(1); - EvalDeltaKeyValue keyValueIndexer = keyValuesIndexer.get(0); - assertThat(keyValueIndexer.key).isEqualTo(key); - - // They should have the same action - assertThat(keyValueAlgod.value.action).isEqualTo(keyValueIndexer.value.action); - - // And the values should be equal, and should equal the expected value - switch(keyValueAlgod.value.action.toString()) { - case "1": - assertThat(keyValueAlgod.value.bytes).isEqualTo(newValue); - assertThat(keyValueAlgod.value.bytes).isEqualTo(keyValueIndexer.value.bytes); - break; - case "2": - assertThat(keyValueAlgod.value.uint).isEqualTo(BigInteger.valueOf(Long.parseLong(newValue))); - assertThat(keyValueAlgod.value.uint).isEqualTo(keyValueIndexer.value.uint); - break; - default: - Assertions.fail("Unknown value action %d.", keyValueAlgod.value.action); - } - } -} diff --git a/src/test/java/com/algorand/algosdk/integration/Indexer.java b/src/test/java/com/algorand/algosdk/integration/Indexer.java deleted file mode 100644 index 706af3716..000000000 --- a/src/test/java/com/algorand/algosdk/integration/Indexer.java +++ /dev/null @@ -1,492 +0,0 @@ -package com.algorand.algosdk.integration; - -import com.algorand.algosdk.crypto.Address; -import com.algorand.algosdk.unit.utils.TestingUtils; -import com.algorand.algosdk.util.Encoder; -import com.algorand.algosdk.util.ResourceUtils; -import com.algorand.algosdk.v2.client.common.*; -import com.algorand.algosdk.v2.client.indexer.*; -import com.algorand.algosdk.v2.client.model.*; -import io.cucumber.java.en.And; -import io.cucumber.java.en.Given; -import io.cucumber.java.en.Then; -import io.cucumber.java.en.When; -import org.apache.commons.lang3.StringUtils; -import org.assertj.core.api.Assertions; - -import java.io.File; -import java.io.IOException; -import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; - -import static com.algorand.algosdk.unit.utils.TestingUtils.searchEnum; -import static org.assertj.core.api.Assertions.assertThat; - -public class Indexer { - Response healthResponse; - Response blockResponse; - Response accountResponse; - Response accountsResponse; - Response assetResponse; - Response assetBalancesResponse; - Response transactionsResponse; - Response assetsResponse; - Response applicationsResponse; - - Response response; - - private final Clients clients; - - public Indexer(Clients clients) { - this.clients = clients; - } - - @When("I use {int} to check the services health") - public void i_use_to_check_the_services_health(Integer index) throws Exception { - healthResponse = clients.indexerClients.get(index).makeHealthCheck().execute(); - } - - @Then("I receive status code {int}") - public void i_receive_status_code(Integer code) { - assertThat(healthResponse.code()).isEqualTo(code); - } - - @When("I use {int} to lookup block {long}") - public void i_request_block_with_indexer(Integer indexer, Long block) throws Exception { - blockResponse = clients.indexerClients.get(indexer).lookupBlock(block).execute(); - } - - @Then("The block was confirmed at {long}, contains {int} transactions, has the previous block hash {string}") - public void the_block_was_confirmed_at_contains_transactions_has_the_previous_block_hash(Long unixTimestamp, Integer numTransactions, String previousBlockHash) { - Block block = blockResponse.body(); - - assertThat(block.timestamp).isEqualTo(unixTimestamp); - assertThat(block.transactions).hasSize(numTransactions); - assertThat(block.previousBlockHash).isEqualTo(Encoder.decodeFromBase64(previousBlockHash)); - } - - @When("I use {int} to lookup account {string} at round {long}") - public void i_lookup_account_with(Integer indexer, String account, Long round) throws Exception { - LookupAccountByID query = clients.indexerClients.get(indexer).lookupAccountByID(new Address(account)); - if (round != 0) { - query.round(round); - } - accountResponse = query.execute(); - } - - @Then("The account has {int} assets, the first is asset {long} has a frozen status of {string} and amount {biginteger}.") - public void the_account_has_num_assets_asset_has_a_frozen_status_of_and_amount(Integer numAssets, Long assetId, String frozenStatus, BigInteger amount) { - AccountResponse response = accountResponse.body(); - - assertThat(response.account.assets).hasSize(numAssets); - AssetHolding holding = response.account.assets.get(0); - assertThat(holding.assetId).isEqualTo(assetId); - assertThat(holding.isFrozen).isEqualTo(Boolean.parseBoolean(frozenStatus)); - assertThat(holding.amount).isEqualTo(amount); - } - - @Then("The account has {long} μalgos and {int} assets, {long} has {biginteger}") - public void the_account_has_μalgos_and_assets(Long amount, Integer numAssets, Long assetId, BigInteger assetAmount) { - AccountResponse response = accountResponse.body(); - assertThat(response.account.assets).hasSize(numAssets); - assertThat(response.account.amount).as("μalgos").isEqualTo(amount); - - if (assetId != 0) { - response.account.assets.forEach(a -> { - if (a.assetId == assetId) { - assertThat(a.amount).as("assets").isEqualTo(assetAmount); - } - }); - } - } - - @Then("The account created {int} assets, the first is asset {long} is named {string} with a total amount of {biginteger} {string}") - public void the_account_created_assets_the_first_is_asset_is_named_with_a_total_amount_of(Integer numAssetsCreated, Long assetId, String assetName, BigInteger assetTotal, String assetUnit) { - AccountResponse response = accountResponse.body(); - assertThat(response.account.createdAssets).hasSize(numAssetsCreated); - Asset asset = response.account.createdAssets.get(0); - assertThat(asset.params.name).isEqualTo(assetName); - assertThat(asset.params.unitName).isEqualTo(assetUnit); - assertThat(asset.params.total).isEqualTo(assetTotal); - } - - @When("I use {int} to lookup asset {long}") - public void i_lookup_asset_with(Integer indexer, Long assetId) throws Exception { - assetResponse = clients.indexerClients.get(indexer).lookupAssetByID(assetId).execute(); - } - - @Then("The asset found has: {string}, {string}, {string}, {long}, {string}, {biginteger}, {string}") - public void the_asset_found_has(String name, String units, String creatorAddress, Long decimals, String defaultFrozen, BigInteger total, String clawbackAddress) { - AssetResponse response = assetResponse.body(); - AssetParams params = response.asset.params; - - assertThat(params.name).isEqualTo(name); - assertThat(params.unitName).isEqualTo(units); - assertThat(params.creator).isEqualTo(creatorAddress); - assertThat(params.decimals).isEqualTo(decimals); - assertThat(params.defaultFrozen).isEqualTo(Boolean.parseBoolean(defaultFrozen)); - assertThat(params.total).isEqualTo(total); - assertThat(params.clawback).isEqualTo(clawbackAddress); - } - - @When("I use {int} to lookup asset balances for {long} with {long}, {long}, {long} and token {string}") - public void i_lookup_asset_balances_for_with_with(Integer indexer, Long assetId, Long currencyGT, Long currencyLT, Long limit, String next) throws Exception { - LookupAssetBalances query = clients.indexerClients.get(indexer).lookupAssetBalances(assetId); - if (currencyGT != 0) { - query.currencyGreaterThan(currencyGT); - } - if (currencyLT != 0) { - query.currencyLessThan(currencyLT); - } - if (limit != 0) { - query.limit(limit); - } - if (StringUtils.isNotEmpty(next)) { - query.next(next); - } - assetBalancesResponse = query.execute(); - } - - @Then("There are {int} with the asset, the first is {string} has {string} and {biginteger}") - public void there_are_with_the_asset_the_first_is_has_and(Integer numResults, String account, String frozenState, BigInteger amount) { - AssetBalancesResponse response = assetBalancesResponse.body(); - - assertThat(response.balances).hasSize(numResults); - - MiniAssetHolding holding = response.balances.get(0); - - assertThat(holding.isFrozen).isEqualTo(Boolean.parseBoolean(frozenState)); - assertThat(holding.amount).isEqualTo(amount); - } - - @When("I get the next page using {int} to lookup asset balances for {long} with {long}, {long}, {long}") - public void i_get_the_next_page_using_to_search_for_asset_balances_with(Integer indexer, Long assetId, Long currencyGT, Long currencyLT, Long limit) throws Exception { - String token = assetBalancesResponse.body().nextToken; - i_lookup_asset_balances_for_with_with(indexer, assetId, currencyGT, currencyLT, limit, token); - } - - @When("I use {int} to search for an account with {long}, {long}, {long}, {long} and token {string}") - public void oldSearchForAccounts(Integer indexer, Long assetId, Long limit, Long gt, Long lt, String token) throws Exception { - searchForAccounts(indexer, assetId, limit, gt, lt, "", 0L, "false", token); - } - - @When("I use {int} to search for an account with {long}, {long}, {long}, {long}, {string}, {long} and token {string}") - public void oldSearchForAccounts2(Integer indexer, Long assetId, Long limit, Long gt, Long lt, String authAddr, Long applicationId, String token) throws Exception { - searchForAccounts(indexer, assetId, limit, gt, lt, authAddr, applicationId, "false", token); - } - - @When("I use {int} to search for an account with {long}, {long}, {long}, {long}, {string}, {long}, {string} and token {string}") - public void searchForAccounts(Integer indexer, Long assetId, Long limit, Long gt, Long lt, String authAddr, Long applicationId, String includeAll, String token) throws Exception { - SearchForAccounts query = clients.indexerClients.get(indexer).searchForAccounts(); - - if (assetId != 0) query.assetId(assetId); - if (limit != 0) query.limit(limit); - if (gt != 0) query.currencyGreaterThan(gt); - if (lt != 0) query.currencyLessThan(lt); - if (StringUtils.isNotEmpty(token)) query.next(token); - if (StringUtils.isNotEmpty(authAddr)) query.authAddr(new Address(authAddr)); - if (applicationId != 0) query.applicationId(applicationId); - if (Boolean.parseBoolean(includeAll)) query.includeAll(Boolean.parseBoolean(includeAll)); - - accountsResponse = query.execute(); - response = accountsResponse; - } - - @Then("There are {int}, the first has {long}, {long}, {long}, {long}, {string}, {long}, {string}, {string}") - public void there_are_the_first_has(Integer numAccounts, Long pendingRewards, Long rewardsBase, Long rewards, Long amountWithoutPendingRewards, String address, Long amount, String status, String type) { - AccountsResponse response = accountsResponse.body(); - - assertThat(response.accounts).hasSize(numAccounts); - - Account account = response.accounts.get(0); - assertThat(account.address.toString()).isEqualTo(address); - assertThat(account.pendingRewards).isEqualTo(pendingRewards); - assertThat(account.rewardBase).isEqualTo(rewardsBase); - assertThat(account.rewards).isEqualTo(rewards); - assertThat(account.amountWithoutPendingRewards).isEqualTo(amountWithoutPendingRewards); - assertThat(account.amount).isEqualTo(amount); - assertThat(account.status).isEqualTo(status); - if (account.sigType != null) { - assertThat(account.sigType).isEqualTo(searchEnum(Enums.SigType.class, type)); - } - } - - @Then("The first account is online and has {string}, {long}, {long}, {long}, {string}, {string}") - public void the_first_account_is_online_and_has(String address, Long keyDilution, Long firstValid, Long lastValid, String voteKey, String selectionKey) { - Account account = accountsResponse.body().accounts.get(0); - assertThat(account.address.toString()).isEqualTo(address); - - AccountParticipation participation = account.participation; - assertThat(participation).isNotNull(); - assertThat(participation.voteKeyDilution).isEqualTo(keyDilution); - assertThat(participation.voteFirstValid).isEqualTo(firstValid); - assertThat(participation.voteLastValid).isEqualTo(lastValid); - assertThat(participation.voteParticipationKey).isEqualTo(Encoder.decodeFromBase64(voteKey)); - assertThat(participation.selectionParticipationKey).isEqualTo(Encoder.decodeFromBase64(selectionKey)); - } - - @Then("I get the next page using {int} to search for an account with {long}, {long}, {long} and {long}") - public void i_get_the_next_page_using_to_search_for_an_account_with_and(Integer indexer, Long assetId, Long limit, Long gt, Long lt) throws Exception { - AccountsResponse response = accountsResponse.body(); - searchForAccounts(indexer, assetId, limit, gt, lt, "", 0L, "false", response.nextToken); - } - - @When("I use {int} to search for transactions with {long}, {string}, {string}, {string}, {string}, {long}, {long}, {long}, {long}, {string}, {string}, {long}, {long}, {string}, {string}, {string} and token {string}") - public void oldSearchForTransactions(Integer indexer, Long limit, String notePrefix, - String txType, String sigType, String txId, Long round, - Long minRound, Long maxRound, Long assetId, - String beforeTime, String afterTime, Long currencyGT, - Long currencyLT, String address, String addressRole, - String excludeCloseTo, String token - ) throws Exception { - searchForTransactions(indexer, limit, notePrefix, txType, sigType, txId, round, minRound, maxRound, assetId, - beforeTime, afterTime, currencyGT, currencyLT, address, addressRole, excludeCloseTo, 0L, - token); - } - - @When("I use {int} to search for transactions with {long}, {string}, {string}, {string}, {string}, {long}, {long}, {long}, {long}, {string}, {string}, {long}, {long}, {string}, {string}, {string}, {long} and token {string}") - public void searchForTransactions(Integer indexer, Long limit, String notePrefix, - String txType, String sigType, String txId, Long round, - Long minRound, Long maxRound, Long assetId, - String beforeTime, String afterTime, Long currencyGT, - Long currencyLT, String address, String addressRole, - String excludeCloseTo, Long applicaitonId, String token - ) throws Exception { - SearchForTransactions query = clients.indexerClients.get(indexer).searchForTransactions(); - - if (limit != 0) query.limit(limit); - if (StringUtils.isNotEmpty(notePrefix)) query.notePrefix(Encoder.decodeFromBase64(notePrefix)); - if (StringUtils.isNotEmpty(txType)) query.txType(searchEnum(Enums.TxType.class, txType)); - if (StringUtils.isNotEmpty(sigType)) query.sigType(searchEnum(Enums.SigType.class, sigType)); - if (StringUtils.isNotEmpty(txId)) query.txid(txId); - if (round != 0) query.round(round); - if (minRound != 0) query.minRound(minRound); - if (maxRound != 0) query.maxRound(maxRound); - if (assetId != 0) query.assetId(assetId); - if (StringUtils.isNotEmpty(beforeTime)) query.beforeTime(Utils.parseDate(beforeTime)); - if (StringUtils.isNotEmpty(afterTime)) query.afterTime(Utils.parseDate(afterTime)); - if (currencyGT != 0) query.currencyGreaterThan(currencyGT); - if (currencyLT != 0) query.currencyLessThan(currencyLT); - if (StringUtils.isNotEmpty(address)) query.address(new Address(address)); - if (StringUtils.isNotEmpty(addressRole)) query.addressRole(Enums.AddressRole.valueOf(addressRole.toUpperCase())); - if (StringUtils.isNotEmpty(excludeCloseTo)) query.excludeCloseTo(Boolean.parseBoolean(excludeCloseTo)); - if (StringUtils.isNotEmpty(token)) query.next(token); - if (applicaitonId != 0) query.applicationId(applicaitonId); - - // This step is followed by multiple 'then' steps, so save the state in two places. - transactionsResponse = query.execute(); - response = transactionsResponse; - } - - @Then("there are {int} transactions in the response, the first is {string}.") - public void there_are_transactions_in_the_response_the_first_is(Integer num, String txid) throws NoSuchAlgorithmException { - TransactionsResponse transactions = transactionsResponse.body(); - - assertThat(transactions.transactions).hasSize(num); - - // Don't check the txid if there weren't supposed to be any results. - if (num == 0) return; - - assertThat(transactions.transactions) - .first() - .hasFieldOrPropertyWithValue("id", txid); - } - - @And("Every transaction has tx-type {string}") - public void every_transaction_has_txtype(String type) { - TransactionsResponse transactions = transactionsResponse.body(); - - transactions.transactions.forEach(tx -> { - assertThat(tx.txType).isEqualTo(Enums.TxType.valueOf(type.toUpperCase())); - }); - } - - @And("Every transaction has sig-type {string}") - public void every_transaction_has_sigtype(String type) { - TransactionsResponse transactions = transactionsResponse.body(); - - transactions.transactions.forEach(tx -> { - switch (type) { - case "sig": - assertThat(tx.signature.sig).isNotEmpty(); - break; - case "msig": - assertThat(tx.signature.multisig).isNotNull(); - break; - case "lsig": - assertThat(tx.signature.logicsig).isNotNull(); - break; - } - ; - } - ); - } - - @And("Every transaction has round {long}") - public void every_transaction_has_round(Long round) { - TransactionsResponse transactions = transactionsResponse.body(); - - // Nothing to check for these results. - if (transactions.transactions.size() == 0) return; - - assertThat(transactions.transactions) - .extracting("confirmedRound") - .containsOnly(round); - } - - @And("Every transaction has round >= {long}") - public void every_transaction_has_round_greater_than(Long minRound) { - transactionsResponse.body().transactions.forEach(tx -> assertThat(tx.confirmedRound).isGreaterThanOrEqualTo(minRound)); - } - - @And("Every transaction has round <= {long}") - public void every_transaction_has_round_less_than(Long maxRound) { - transactionsResponse.body().transactions.forEach(tx -> assertThat(tx.confirmedRound).isLessThanOrEqualTo(maxRound)); - } - - @And("Every transaction works with asset-id {long}") - public void every_transaction_works_with_asset_id(Long assetId) { - transactionsResponse.body().transactions.forEach(tx -> { - if (tx.createdAssetIndex != null) { - assertThat(tx.createdAssetIndex).isEqualTo(assetId); - } - if (tx.assetTransferTransaction != null) { - assertThat(tx.assetTransferTransaction.assetId).isEqualTo(assetId); - } - }); - } - - @And("Every transaction is newer than {string}") - public void every_transaction_is_newer_than(String dateString) { - Instant i = Instant.parse(dateString); - transactionsResponse.body().transactions.forEach(tx -> { - assertThat(tx.roundTime).isGreaterThan(i.getEpochSecond()); - }); - } - - @And("Every transaction is older than {string}") - public void every_transaction_is_older_than(String dateString) { - Instant i = Instant.parse(dateString); - transactionsResponse.body().transactions.forEach(tx -> { - assertThat(tx.roundTime).isLessThan(i.getEpochSecond()); - }); - } - - @And("Every transaction moves between {biginteger} and {biginteger} currency") - public void every_transaction_moves_between_and_currency(BigInteger min, BigInteger max) { - // Normalize in case max isn't set. - BigInteger maxUnsignedLong = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.valueOf(2)); - final BigInteger normalizedMax = (max.longValue() == 0) ? maxUnsignedLong : max; - - transactionsResponse.body().transactions.forEach(tx -> { - switch (tx.txType) { - case PAY: - assertThat(BigInteger.valueOf(tx.paymentTransaction.amount)).isBetween(min, normalizedMax); - break; - case AXFER: - assertThat(tx.assetTransferTransaction.amount).isBetween(min, normalizedMax); - break; - default: - Assertions.fail("Only transactions that move currency should match this."); - } - }); - } - - @When("I use {int} to search for all {string} transactions") - public void i_use_to_search_for_all_transactions(Integer indexer, String account) throws Exception { - LookupAccountTransactions query = clients.indexerClients.get(indexer).lookupAccountTransactions(new Address(account)); - transactionsResponse = query.execute(); - } - - @When("I use {int} to search for all {long} asset transactions") - public void i_use_to_search_for_all_asset_transactions(Integer indexer, Long assetId) throws Exception { - LookupAssetTransactions query = clients.indexerClients.get(indexer).lookupAssetTransactions(assetId); - transactionsResponse = query.execute(); - } - - @And("I get the next page using {int} to search for transactions with {long} and {long}") - public void i_get_the_next_page_using_to_search_for_transactions_with_and(Integer indexer, Long limit, Long maxRound) throws Exception { - String next = transactionsResponse.body().nextToken; - - // Reuse the all-args wrapper, injecting the next token - searchForTransactions(indexer, limit, "", "", "", "", - 0L, 0L, maxRound, 0L, "", "", 0L, 0L, - "", "", "", 0L, next); - } - - @When("I use {int} to search for assets with {long}, {long}, {string}, {string}, {string}, and token {string}") - public void i_use_to_search_for_assets_with_and_token(Integer indexer, Long limit, Long assetId, String creator, String name, String unit, String token) throws Exception { - SearchForAssets query = clients.indexerClients.get(indexer).searchForAssets(); - - if (limit != 0) { - query.limit(limit); - } - if (assetId != 0) { - query.assetId(assetId); - } - if (StringUtils.isNotEmpty(creator)) { - query.creator(creator); - } - if (StringUtils.isNotEmpty(name)) { - query.name(name); - } - if (StringUtils.isNotEmpty(unit)) { - query.unit(unit); - } - if (StringUtils.isNotEmpty(token)) { - query.next(token); - } - assetsResponse = query.execute(); - } - - @Then("there are {int} assets in the response, the first is {long}.") - public void there_are_assets_in_the_response_the_first_is(Integer num, Long assetId) { - AssetsResponse response = assetsResponse.body(); - assertThat(response.assets).hasSize(num); - response.assets.forEach(a -> assertThat(a.index).isEqualTo(assetId)); - } - - - @When("I use {int} to search for applications with {long}, {long}, and token {string}") - public void i_use_to_search_for_applications_with_and_token_deprecated(Integer indexer, Long limit, Long applicationId, String token) throws Exception { - i_use_to_search_for_applications_with_and_token(indexer, limit, applicationId, "false", token); - } - - @When("I use {int} to search for applications with {long}, {long}, {string} and token {string}") - public void i_use_to_search_for_applications_with_and_token(Integer indexer, Long limit, Long applicationId, String includeAll, String token) throws Exception { - SearchForApplications query = clients.indexerClients.get(indexer).searchForApplications(); - - if (limit != 0) query.limit(limit); - if (applicationId != 0) query.applicationId(applicationId); - if (Boolean.parseBoolean(includeAll)) query.includeAll(Boolean.parseBoolean(includeAll)); - if (StringUtils.isNotEmpty(token)) query.next(token); - - response = query.execute(); - } - - @When("I use {int} to lookup application with {long}") - public void i_use_to_lookup_application_with(Integer indexer, Long applicationId) throws Exception { - i_use_to_lookup_application_with_and(indexer, applicationId, "false"); - } - - @When("I use {int} to lookup application with {long} and {string}") - public void i_use_to_lookup_application_with_and(Integer indexer, Long applicationId, String includeAll) throws Exception { - LookupApplicationByID query = clients.indexerClients.get(indexer).lookupApplicationByID(applicationId); - - if (Boolean.parseBoolean(includeAll)) query.includeAll(Boolean.parseBoolean(includeAll)); - - response = query.execute(); - } - - @Then("the parsed response should equal {string}.") - public void the_parsed_response_should_equal(String jsonFile) throws IOException { - File f = new File("src/test/resources/" + jsonFile); - assertThat(f).canRead(); - String json = response.toString(); - TestingUtils.verifyResponse(response, f); - } - -} diff --git a/src/test/java/com/algorand/algosdk/integration/Stepdefs.java b/src/test/java/com/algorand/algosdk/integration/Stepdefs.java index 278878e03..c84c1f8bc 100644 --- a/src/test/java/com/algorand/algosdk/integration/Stepdefs.java +++ b/src/test/java/com/algorand/algosdk/integration/Stepdefs.java @@ -29,14 +29,11 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; -import org.threeten.bp.LocalDate; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; @@ -348,65 +345,12 @@ public void transactionParameters(int fee, int fv, int lv, String gh, String to, } } - @Given("key registration transaction parameters {int} {int} {int} {string} {string} {string} {int} {int} {int} {string} {string}") - public void keyregTxnParameters(int fee, int fv, int lv, String gh, String votepk, String vrfpk, int votefst, int votelst, int votekd, String gen, String note) throws GeneralSecurityException, NoSuchAlgorithmException{ - this.fee = BigInteger.valueOf(fee); - this.fv = BigInteger.valueOf(fv); - this.lv = BigInteger.valueOf(lv); - this.gh = new Digest(Encoder.decodeFromBase64(gh)); - this.votepk = new ParticipationPublicKey(Encoder.decodeFromBase64(votepk)); - this.vrfpk = new VRFPublicKey(Encoder.decodeFromBase64(vrfpk)); - this.votefst = BigInteger.valueOf(votefst); - this.votelst = BigInteger.valueOf(votelst); - this.votekd = BigInteger.valueOf(votekd); - if (!gen.equals("none")) { - this.gen = gen; - } - if (!note.equals("none")) { - this.note = Encoder.decodeFromBase64(note); - } - } - @Given("mnemonic for private key {string}") public void mn_for_sk(String mn) throws GeneralSecurityException{ account = new Account(mn); pk = account.getAddress(); } - @When("I create the payment transaction") - public void createPaytxn() throws NoSuchAlgorithmException, JsonProcessingException, IOException{ - txn = Transaction.PaymentTransactionBuilder() - .sender(pk) - .fee(fee) - .firstValid(fv) - .lastValid(lv) - .note(note) - .genesisID(gen) - .genesisHash(gh) - .amount(amt) - .receiver(to) - .closeRemainderTo(close) - .build(); - } - - @When("I create the key registration transaction") - public void createKeyregTxn() throws NoSuchAlgorithmException, JsonProcessingException, IOException{ - txn = Transaction.KeyRegistrationTransactionBuilder() - .sender(pk) - .fee(fee) - .firstValid(fv) - .lastValid(lv) - .note(note) - .genesisID(gen) - .genesisHash(gh) - .participationPublicKey(votepk) - .selectionPublicKey(vrfpk) - .voteFirst(votefst) - .voteLast(votelst) - .voteKeyDilution(votekd) - .build(); - } - @Given("default V2 key registration transaction {string}") public void default_v2_key_registration_transaction(String type) throws NoSuchAlgorithmException, JsonProcessingException, IOException{ getParams(); @@ -824,6 +768,7 @@ public void sendMsigTxn() throws JsonProcessingException, ApiException{ } } + // TODO: this needs to be modified/removed when v1 is no longer supported!!! @Then("the transaction should go through") public void checkTxn() throws Exception { String ans = acl.pendingTransactionInformation(txid).getFrom(); @@ -831,7 +776,7 @@ public void checkTxn() throws Exception { waitForAlgodTransactionProcessingToComplete(); String senderFromResponse = acl.transactionInformation(txn.sender.toString(), txid).getFrom(); assertThat(senderFromResponse).isEqualTo(txn.sender.toString()); - assertThat(acl.transaction(txid).getFrom()).isEqualTo(senderFromResponse); + // assertThat(acl.transaction(txid).getFrom()).isEqualTo(senderFromResponse); } /** @@ -843,12 +788,6 @@ private static void waitForAlgodTransactionProcessingToComplete() throws Excepti Thread.sleep(500); } - @Then("I can get the transaction by ID") - public void txnByID() throws Exception { - waitForAlgodTransactionProcessingToComplete(); - assertThat(acl.transaction(txid).getFrom()).isEqualTo(pk.toString()); - } - @Then("the transaction should not go through") public void txnFail() { assertThat(err).isTrue(); @@ -894,74 +833,6 @@ public void signMsigBothEqual() throws JsonProcessingException, com.algorand.alg kcl.deleteMultisig(req); } - @When("I read a transaction {string} from file {string}") - public void readTxn(String encodedTxn, String num) throws IOException { - String path = System.getProperty("user.dir"); - Path p = Paths.get(path); - this.num = num; - path = p.getParent() + "/temp/raw" + this.num + ".tx"; - FileInputStream inputStream = new FileInputStream(path); - File file = new File(path); - byte[] data = new byte[(int) file.length()]; - inputStream.read(data); - stx = Encoder.decodeFromMsgPack(data, SignedTransaction.class); - inputStream.close(); - } - - @When("I write the transaction to file") - public void writeTxn() throws JsonProcessingException, IOException{ - String path = System.getProperty("user.dir"); - Path p = Paths.get(path); - path = p.getParent() + "/temp/raw" + this.num + ".tx"; - byte[] data = Encoder.encodeToMsgPack(stx); - FileOutputStream out = new FileOutputStream(path); - out.write(data); - out.close(); - } - - @Then("the transaction should still be the same") - public void checkEnc() throws IOException{ - String path = System.getProperty("user.dir"); - Path p = Paths.get(path); - path = p.getParent() + "/temp/raw" + this.num + ".tx"; - FileInputStream inputStream = new FileInputStream(path); - File file = new File(path); - byte[] data = new byte[(int) file.length()]; - inputStream.read(data); - SignedTransaction stxnew = Encoder.decodeFromMsgPack(data, SignedTransaction.class); - inputStream.close(); - - path = p.getParent() + "/temp/old" + this.num + ".tx"; - inputStream = new FileInputStream(path); - file = new File(path); - data = new byte[(int) file.length()]; - inputStream.read(data); - SignedTransaction stxold = Encoder.decodeFromMsgPack(data, SignedTransaction.class); - inputStream.close(); - assertThat(stxnew).isEqualTo(stxold); - } - - @Then("I do my part") - public void signSaveTxn() throws IOException, JsonProcessingException, NoSuchAlgorithmException, com.algorand.algosdk.kmd.client.ApiException, Exception{ - String path = System.getProperty("user.dir"); - Path p = Paths.get(path); - path = p.getParent() + "/temp/txn.tx"; - FileInputStream inputStream = new FileInputStream(path); - File file = new File(path); - byte[] data = new byte[(int) file.length()]; - inputStream.read(data); - inputStream.close(); - - txn = Encoder.decodeFromMsgPack(data, Transaction.class); - exportKeyAndSetAccount(txn.sender); - - stx = account.signTransaction(txn); - data = Encoder.encodeToMsgPack(stx); - FileOutputStream out = new FileOutputStream(path); - out.write(data); - out.close(); - } - @Then("the node should be healthy") public void nodeHealth() throws ApiException{ acl.healthCheck(); @@ -979,20 +850,6 @@ public void txnsByAddrRound() throws ApiException{ //Assert.assertTrue(acl.transactions(addresses.get(0), BigInteger.valueOf(1), acl.getStatus().getLastRound(), null, null, BigInteger.valueOf(10)).getTransactions() instanceof List); } - @Then("I get transactions by address only") - public void txnsByAddrOnly() throws ApiException{ - assertThat(acl.transactions(addresses.get(0), null, null, null, null, BigInteger.valueOf(10)).getTransactions()) - .isInstanceOf(List.class); - //Assert.assertTrue(acl.transactions(addresses.get(0), null, null, null, null, BigInteger.valueOf(10)).getTransactions() instanceof List); - } - - @Then("I get transactions by address and date") - public void txnsByAddrDate() throws ApiException{ - assertThat(acl.transactions(addresses.get(0), null, null, LocalDate.now(), LocalDate.now(), BigInteger.valueOf(10)).getTransactions()) - .isInstanceOf(List.class); - //Assert.assertTrue(acl.transactions(addresses.get(0), null, null, LocalDate.now(), LocalDate.now(), BigInteger.valueOf(10)).getTransactions() instanceof List); - } - @Then("I get pending transactions") public void pendingTxns() throws ApiException{ assertThat(acl.getPendingTransactions(BigInteger.valueOf(10)).getTruncatedTxns()) @@ -1147,13 +1004,6 @@ public void newAccInfo() throws ApiException, NoSuchAlgorithmException, com.algo kcl.deleteKey(req); } - @When("I get recent transactions, limited by {int} transactions") - public void i_get_recent_transactions_limited_by_count(int cnt) throws ApiException { - assertThat(acl.transactions(addresses.get(0), null, null, null, null, BigInteger.valueOf(cnt)).getTransactions()) - .isInstanceOf(List.class); - //Assert.assertTrue(acl.transactions(addresses.get(0), null, null, null, null, BigInteger.valueOf(cnt)).getTransactions() instanceof List); - } - @Given("asset test fixture") public void asset_test_fixture() { // Implemented by the construction of Stepdefs; diff --git a/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java b/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java index 611f622a1..59bae3fd3 100644 --- a/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java +++ b/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java @@ -1,10 +1,10 @@ package com.algorand.algosdk.unit; import com.algorand.algosdk.crypto.Address; -import com.algorand.algosdk.unit.utils.QueryMapper; import com.algorand.algosdk.unit.utils.TestingUtils; import com.algorand.algosdk.v2.client.algod.*; import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.model.Enums; import io.cucumber.java.en.When; import java.security.NoSuchAlgorithmException; @@ -78,7 +78,24 @@ public void accountAssetInformation(String string, Integer int1) throws NoSuchAl @When("we make an Account Information call against account {string} with exclude {string}") public void accountInformation(String string, String string2) throws NoSuchAlgorithmException { AccountInformation aiq = algodClient.AccountInformation(new Address(string)); - if (TestingUtils.notEmpty(string2)) aiq.exclude(QueryMapper.getExclude(string2)); + if (TestingUtils.notEmpty(string2)) aiq.exclude(Enums.Exclude.forValue(string2)); ps.q = aiq; } + + @When("we make a GetTransactionProof call for round {long} txid {string} and hashtype {string}") + public void getTransactionProof(Long round, String txid, String hashType) { + GetTransactionProof gtp = algodClient.GetTransactionProof(round, txid); + if (TestingUtils.notEmpty(hashType)) gtp.hashtype(Enums.Hashtype.forValue(hashType)); + ps.q = gtp; + } + + @When("we make a GetLightBlockHeaderProof call for round {long}") + public void getLightBlockHeaderProof(Long round) { + ps.q = algodClient.GetLightBlockHeaderProof(round); + } + + @When("we make a GetStateProof call for round {long}") + public void getStateProof(Long round) { + ps.q = algodClient.GetStateProof(round); + } } diff --git a/src/test/java/com/algorand/algosdk/unit/IndexerPaths.java b/src/test/java/com/algorand/algosdk/unit/IndexerPaths.java index bdcec1fd9..8c9cb43e8 100644 --- a/src/test/java/com/algorand/algosdk/unit/IndexerPaths.java +++ b/src/test/java/com/algorand/algosdk/unit/IndexerPaths.java @@ -1,7 +1,6 @@ package com.algorand.algosdk.unit; import com.algorand.algosdk.crypto.Address; -import com.algorand.algosdk.unit.utils.QueryMapper; import com.algorand.algosdk.unit.utils.TestingUtils; import com.algorand.algosdk.util.Encoder; import com.algorand.algosdk.v2.client.common.IndexerClient; @@ -155,8 +154,8 @@ public void lookupAssetTransactions(Long assetId, String notePrefix, String txTy LookupAssetTransactions q = this.indexerClient.lookupAssetTransactions(assetId); if (TestingUtils.notEmpty(address)) q.address(new Address(address)); if (TestingUtils.notEmpty(notePrefix)) q.notePrefix(Encoder.decodeFromBase64(notePrefix)); - if (TestingUtils.notEmpty(txType)) q.txType(QueryMapper.getTxType(txType)); - if (TestingUtils.notEmpty(sigType)) q.sigType(QueryMapper.getSigType(sigType)); + if (TestingUtils.notEmpty(txType)) q.txType(Enums.TxType.forValue(txType)); + if (TestingUtils.notEmpty(sigType)) q.sigType(Enums.SigType.forValue(sigType)); if (TestingUtils.notEmpty(txid)) q.txid(txid); if (TestingUtils.notEmpty(round)) q.round(round); if (TestingUtils.notEmpty(minRound)) q.minRound(minRound); @@ -166,7 +165,7 @@ public void lookupAssetTransactions(Long assetId, String notePrefix, String txTy if (TestingUtils.notEmpty(afterTime)) q.afterTime(Utils.parseDate(afterTime)); if (TestingUtils.notEmpty(currencyGT)) q.currencyGreaterThan(currencyGT); if (TestingUtils.notEmpty(currencyLT)) q.currencyLessThan(currencyLT); - if (TestingUtils.notEmpty(addressRole)) q.addressRole(QueryMapper.getAddressRole(addressRole)); + if (TestingUtils.notEmpty(addressRole)) q.addressRole(Enums.AddressRole.forValue(addressRole)); if (TestingUtils.notEmpty(excludeCloseTo)) q.excludeCloseTo(excludeCloseTo.equals("true")); if (TestingUtils.notEmpty(rekeyTo)) q.rekeyTo(rekeyTo.equals("true")); ps.q = q; @@ -176,8 +175,8 @@ public void lookupAssetTransactions(Long assetId, String notePrefix, String txTy public void lookupAccountTransactions(String account, String notePrefix, String txType, String sigType, String txid, Long round, Long minRound, Long maxRound, Long limit, String beforeTime, String afterTime, Long currencyGT, Long currencyLT, Long assetId, String rekeyTo) throws NoSuchAlgorithmException, ParseException { LookupAccountTransactions q = this.indexerClient.lookupAccountTransactions(new Address(account)); if (TestingUtils.notEmpty(notePrefix)) q.notePrefix(Encoder.decodeFromBase64(notePrefix)); - if (TestingUtils.notEmpty(txType)) q.txType(QueryMapper.getTxType(txType)); - if (TestingUtils.notEmpty(sigType)) q.sigType(QueryMapper.getSigType(sigType)); + if (TestingUtils.notEmpty(txType)) q.txType(Enums.TxType.forValue(txType)); + if (TestingUtils.notEmpty(sigType)) q.sigType(Enums.SigType.forValue(sigType)); if (TestingUtils.notEmpty(txid)) q.txid(txid); if (TestingUtils.notEmpty(round)) q.round(round); if (TestingUtils.notEmpty(minRound)) q.minRound(minRound); @@ -213,8 +212,8 @@ public void searchForTransactions(String address, String notePrefix, String txTy SearchForTransactions q = this.indexerClient.searchForTransactions(); if (TestingUtils.notEmpty(address)) q.address(new Address(address)); if (TestingUtils.notEmpty(notePrefix)) q.notePrefix(Encoder.decodeFromBase64(notePrefix)); - if (TestingUtils.notEmpty(txType)) q.txType(QueryMapper.getTxType(txType)); - if (TestingUtils.notEmpty(sigType)) q.sigType(QueryMapper.getSigType(sigType)); + if (TestingUtils.notEmpty(txType)) q.txType(Enums.TxType.forValue(txType)); + if (TestingUtils.notEmpty(sigType)) q.sigType(Enums.SigType.forValue(sigType)); if (TestingUtils.notEmpty(txid)) q.txid(txid); if (TestingUtils.notEmpty(round)) q.round(round); if (TestingUtils.notEmpty(minRound)) q.minRound(minRound); @@ -225,7 +224,7 @@ public void searchForTransactions(String address, String notePrefix, String txTy if (TestingUtils.notEmpty(currencyGT)) q.currencyGreaterThan(currencyGT); if (TestingUtils.notEmpty(currencyLT)) q.currencyLessThan(currencyLT); if (TestingUtils.notEmpty(assetID)) q.assetId(assetID); - if (TestingUtils.notEmpty(addressRole)) q.addressRole(QueryMapper.getAddressRole(addressRole)); + if (TestingUtils.notEmpty(addressRole)) q.addressRole(Enums.AddressRole.forValue(addressRole)); if (TestingUtils.notEmpty(excludeCloseTo)) q.excludeCloseTo(excludeCloseTo.equals("true")); if (TestingUtils.notEmpty(rekeyTo)) q.rekeyTo(rekeyTo.equals("true")); ps.q = q; @@ -285,7 +284,7 @@ public void lookupAccountByID(String string, String string2) throws NoSuchAlgori if (TestingUtils.notEmpty(string2)) { ArrayList excludes = new ArrayList(); for (String excld : string2.split(",")) { - excludes.add(QueryMapper.getExclude(excld)); + excludes.add(Enums.Exclude.forValue(excld)); } q.exclude(excludes); } @@ -320,7 +319,7 @@ public void searchForAccounts(String string) { if (TestingUtils.notEmpty(string)) { ArrayList excludes = new ArrayList(); for (String excld : string.split(",")) { - excludes.add(QueryMapper.getExclude(excld)); + excludes.add(Enums.Exclude.forValue(excld)); } q.exclude(excludes); } diff --git a/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java b/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java new file mode 100644 index 000000000..6e9ffde03 --- /dev/null +++ b/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java @@ -0,0 +1,37 @@ +package com.algorand.algosdk.unit; + +import com.algorand.algosdk.crypto.LogicsigSignature; +import com.algorand.algosdk.util.Encoder; + +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProgramSanityCheck { + byte[] seeminglyProgram; + String actualErrMsg; + + @Given("a base64 encoded program bytes for heuristic sanity check {string}") + public void takeB64encodedBytes(String b64encodedBytes) { + seeminglyProgram = Encoder.decodeFromBase64(b64encodedBytes); + } + + @When("I start heuristic sanity check over the bytes") + public void heuristicCheckOverBytes() { + try { + new LogicsigSignature(seeminglyProgram); + } catch (Exception e) { + actualErrMsg = e.getMessage(); + } + } + + @Then("if the heuristic sanity check throws an error, the error contains {string}") + public void checkErrorIfMatching(String errMsg) { + if (errMsg != null && !errMsg.isEmpty()) + assertThat(actualErrMsg).contains(errMsg); + else + assertThat(actualErrMsg).isNullOrEmpty(); + } +} diff --git a/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java b/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java index 65889dfe4..ae4f37c96 100644 --- a/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java +++ b/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java @@ -10,6 +10,7 @@ import com.algorand.algosdk.v2.client.common.Response; import com.algorand.algosdk.v2.client.model.DryrunResponse; import com.algorand.algosdk.v2.client.model.DryrunTxnResult; +import com.algorand.algosdk.v2.client.model.StateProof; import com.google.common.io.Files; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; @@ -71,6 +72,9 @@ public void we_make_any_call_to(String client, String endpoint) throws Exception switch(client) { case "indexer": { switch(endpoint) { + case "lookupBlock": + response = indexer.lookupBlock(1234L).execute(); + break; case "lookupAccountByID": response = indexer.lookupAccountByID(new Address()).execute(); break; @@ -165,7 +169,8 @@ public void we_make_any_call_to(String client, String endpoint) throws Exception response = algod.TealDryrun().execute(); break; case "Proof": - response = algod.GetProof(0L, "").execute(); + case "GetTransactionProof": + response = algod.GetTransactionProof(0L, "").execute(); break; case "AccountInformation": response = algod.AccountInformation(new Address()).execute(); @@ -176,6 +181,16 @@ public void we_make_any_call_to(String client, String endpoint) throws Exception case "AccountAssetInformation": response = algod.AccountAssetInformation(new Address(), 0L).execute(); break; + case "GetBlock": + response = algod.GetBlock(1234L).execute(); + break; + case "GetLightBlockHeaderProof": + response = algod.GetLightBlockHeaderProof(1234L).execute(); + break; + case "GetStateProof": + Response r = algod.GetStateProof(1234L).execute(); + response = r; + break; default: Assertions.fail("Unsupported algod endpoint: " + endpoint); } diff --git a/src/test/java/com/algorand/algosdk/unit/RunCucumberUnitTest.java b/src/test/java/com/algorand/algosdk/unit/RunCucumberUnitTest.java index cd92dad4d..e287616ec 100644 --- a/src/test/java/com/algorand/algosdk/unit/RunCucumberUnitTest.java +++ b/src/test/java/com/algorand/algosdk/unit/RunCucumberUnitTest.java @@ -5,6 +5,6 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(publish = false, plugin = {"progress"}, tags = "@disabled.by.default", extraGlue = "com.algorand.algosdk.cucumber.shared") +@CucumberOptions(publish = true, plugin = {"progress"}, tags = "@disabled.by.default", extraGlue = "com.algorand.algosdk.cucumber.shared") public class RunCucumberUnitTest { } diff --git a/src/test/java/com/algorand/algosdk/unit/utils/QueryMapper.java b/src/test/java/com/algorand/algosdk/unit/utils/QueryMapper.java deleted file mode 100644 index 15f18b1a1..000000000 --- a/src/test/java/com/algorand/algosdk/unit/utils/QueryMapper.java +++ /dev/null @@ -1,689 +0,0 @@ -/* - * THIS FILE IS GENERATED BY QueryMapperGenerator!!!!!!!!!!!!!!!! - */ -package com.algorand.algosdk.unit.utils; - -import java.security.NoSuchAlgorithmException; -import com.fasterxml.jackson.core.JsonProcessingException; -import java.text.ParseException; -import java.util.ArrayList; - -import com.algorand.algosdk.v2.client.common.Utils; - -import com.algorand.algosdk.crypto.Address; -import com.algorand.algosdk.util.Encoder; -import com.algorand.algosdk.v2.client.algod.*; -import com.algorand.algosdk.v2.client.indexer.*; -import com.algorand.algosdk.v2.client.model.Enums; -import com.algorand.algosdk.v2.client.common.*; - -public class QueryMapper { - - public static Query getClass(String name, IndexerClient client, String args[]) throws NoSuchAlgorithmException { - switch (name) { - case "makeHealthCheck": - return client.makeHealthCheck(); - case "searchForAccounts": - return client.searchForAccounts(); - case "lookupAccountByID": - return client.lookupAccountByID(new Address(args[0])); - case "lookupAccountAssets": - return client.lookupAccountAssets(new Address(args[0])); - case "lookupAccountCreatedAssets": - return client.lookupAccountCreatedAssets(new Address(args[0])); - case "lookupAccountAppLocalStates": - return client.lookupAccountAppLocalStates(new Address(args[0])); - case "lookupAccountCreatedApplications": - return client.lookupAccountCreatedApplications(new Address(args[0])); - case "lookupAccountTransactions": - return client.lookupAccountTransactions(new Address(args[0])); - case "searchForApplications": - return client.searchForApplications(); - case "lookupApplicationByID": - return client.lookupApplicationByID(Long.valueOf(args[0])); - case "lookupApplicationLogsByID": - return client.lookupApplicationLogsByID(Long.valueOf(args[0])); - case "searchForAssets": - return client.searchForAssets(); - case "lookupAssetByID": - return client.lookupAssetByID(Long.valueOf(args[0])); - case "lookupAssetBalances": - return client.lookupAssetBalances(Long.valueOf(args[0])); - case "lookupAssetTransactions": - return client.lookupAssetTransactions(Long.valueOf(args[0])); - case "lookupBlock": - return client.lookupBlock(Long.valueOf(args[0])); - case "lookupTransaction": - return client.lookupTransaction(args[0]); - case "searchForTransactions": - return client.searchForTransactions(); - } - return null; - } - - public static Query getClass(String name, AlgodClient client, String args[]) throws NoSuchAlgorithmException { - switch (name) { - case "HealthCheck": - return client.HealthCheck(); - case "Metrics": - return client.Metrics(); - case "GetGenesis": - return client.GetGenesis(); - case "SwaggerJSON": - return client.SwaggerJSON(); - case "GetVersion": - return client.GetVersion(); - case "AccountInformation": - return client.AccountInformation(new Address(args[0])); - case "AccountAssetInformation": - return client.AccountAssetInformation(new Address(args[0]), Long.valueOf(args[1])); - case "AccountApplicationInformation": - return client.AccountApplicationInformation(new Address(args[0]), Long.valueOf(args[1])); - case "GetPendingTransactionsByAddress": - return client.GetPendingTransactionsByAddress(new Address(args[0])); - case "GetBlock": - return client.GetBlock(Long.valueOf(args[0])); - case "GetProof": - return client.GetProof(Long.valueOf(args[0]), args[1]); - case "GetSupply": - return client.GetSupply(); - case "GetStatus": - return client.GetStatus(); - case "WaitForBlock": - return client.WaitForBlock(Long.valueOf(args[0])); - case "RawTransaction": - return client.RawTransaction(); - case "TransactionParams": - return client.TransactionParams(); - case "GetPendingTransactions": - return client.GetPendingTransactions(); - case "PendingTransactionInformation": - return client.PendingTransactionInformation(args[0]); - case "GetApplicationByID": - return client.GetApplicationByID(Long.valueOf(args[0])); - case "GetAssetByID": - return client.GetAssetByID(Long.valueOf(args[0])); - case "TealCompile": - return client.TealCompile(); - case "TealDryrun": - return client.TealDryrun(); - } - return null; - } - - public static void setValue(Query q, String className, String property, String value) throws ParseException, NoSuchAlgorithmException, JsonProcessingException { - switch (className) { - case "makeHealthCheck": - switch (property) { - } - break; - case "searchForAccounts": - switch (property) { - case "application-id": - ((SearchForAccounts)q).applicationId(Long.valueOf(value)); - break; - case "asset-id": - ((SearchForAccounts)q).assetId(Long.valueOf(value)); - break; - case "auth-addr": - ((SearchForAccounts)q).authAddr(new Address(value)); - break; - case "currency-greater-than": - ((SearchForAccounts)q).currencyGreaterThan(Long.valueOf(value)); - break; - case "currency-less-than": - ((SearchForAccounts)q).currencyLessThan(Long.valueOf(value)); - break; - case "exclude": - ArrayList vals = new ArrayList(); - for (String t : value.split(",")) { - vals.add(getExclude(t)); - } - ((SearchForAccounts)q).exclude(vals); - break; - case "include-all": - ((SearchForAccounts)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((SearchForAccounts)q).limit(Long.valueOf(value)); - break; - case "next": - ((SearchForAccounts)q).next(value); - break; - case "round": - ((SearchForAccounts)q).round(Long.valueOf(value)); - break; - } - break; - case "lookupAccountByID": - switch (property) { - case "exclude": - ArrayList vals = new ArrayList(); - for (String t : value.split(",")) { - vals.add(getExclude(t)); - } - ((SearchForAccounts)q).exclude(vals); - break; - case "include-all": - ((LookupAccountByID)q).includeAll(Boolean.valueOf(value)); - break; - case "round": - ((LookupAccountByID)q).round(Long.valueOf(value)); - break; - } - break; - case "lookupAccountAssets": - switch (property) { - case "asset-id": - ((LookupAccountAssets)q).assetId(Long.valueOf(value)); - break; - case "include-all": - ((LookupAccountAssets)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAccountAssets)q).limit(Long.valueOf(value)); - break; - case "next": - ((LookupAccountAssets)q).next(value); - break; - } - break; - case "lookupAccountCreatedAssets": - switch (property) { - case "asset-id": - ((LookupAccountCreatedAssets)q).assetId(Long.valueOf(value)); - break; - case "include-all": - ((LookupAccountCreatedAssets)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAccountCreatedAssets)q).limit(Long.valueOf(value)); - break; - case "next": - ((LookupAccountCreatedAssets)q).next(value); - break; - } - break; - case "lookupAccountAppLocalStates": - switch (property) { - case "application-id": - ((LookupAccountAppLocalStates)q).applicationId(Long.valueOf(value)); - break; - case "include-all": - ((LookupAccountAppLocalStates)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAccountAppLocalStates)q).limit(Long.valueOf(value)); - break; - case "next": - ((LookupAccountAppLocalStates)q).next(value); - break; - } - break; - case "lookupAccountCreatedApplications": - switch (property) { - case "application-id": - ((LookupAccountCreatedApplications)q).applicationId(Long.valueOf(value)); - break; - case "include-all": - ((LookupAccountCreatedApplications)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAccountCreatedApplications)q).limit(Long.valueOf(value)); - break; - case "next": - ((LookupAccountCreatedApplications)q).next(value); - break; - } - break; - case "lookupAccountTransactions": - switch (property) { - case "after-time": - ((LookupAccountTransactions)q).afterTime(Utils.parseDate(value)); - break; - case "asset-id": - ((LookupAccountTransactions)q).assetId(Long.valueOf(value)); - break; - case "before-time": - ((LookupAccountTransactions)q).beforeTime(Utils.parseDate(value)); - break; - case "currency-greater-than": - ((LookupAccountTransactions)q).currencyGreaterThan(Long.valueOf(value)); - break; - case "currency-less-than": - ((LookupAccountTransactions)q).currencyLessThan(Long.valueOf(value)); - break; - case "limit": - ((LookupAccountTransactions)q).limit(Long.valueOf(value)); - break; - case "max-round": - ((LookupAccountTransactions)q).maxRound(Long.valueOf(value)); - break; - case "min-round": - ((LookupAccountTransactions)q).minRound(Long.valueOf(value)); - break; - case "next": - ((LookupAccountTransactions)q).next(value); - break; - case "note-prefix": - ((LookupAccountTransactions)q).notePrefix(Encoder.decodeFromBase64(value)); - break; - case "rekey-to": - ((LookupAccountTransactions)q).rekeyTo(Boolean.valueOf(value)); - break; - case "round": - ((LookupAccountTransactions)q).round(Long.valueOf(value)); - break; - case "sig-type": - ((LookupAccountTransactions)q).sigType(getSigType(value)); - break; - case "tx-type": - ((LookupAccountTransactions)q).txType(getTxType(value)); - break; - case "txid": - ((LookupAccountTransactions)q).txid(value); - break; - } - break; - case "searchForApplications": - switch (property) { - case "application-id": - ((SearchForApplications)q).applicationId(Long.valueOf(value)); - break; - case "creator": - ((SearchForApplications)q).creator(value); - break; - case "include-all": - ((SearchForApplications)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((SearchForApplications)q).limit(Long.valueOf(value)); - break; - case "next": - ((SearchForApplications)q).next(value); - break; - } - break; - case "lookupApplicationByID": - switch (property) { - case "include-all": - ((LookupApplicationByID)q).includeAll(Boolean.valueOf(value)); - break; - } - break; - case "lookupApplicationLogsByID": - switch (property) { - case "limit": - ((LookupApplicationLogsByID)q).limit(Long.valueOf(value)); - break; - case "max-round": - ((LookupApplicationLogsByID)q).maxRound(Long.valueOf(value)); - break; - case "min-round": - ((LookupApplicationLogsByID)q).minRound(Long.valueOf(value)); - break; - case "next": - ((LookupApplicationLogsByID)q).next(value); - break; - case "sender-address": - ((LookupApplicationLogsByID)q).senderAddress(new Address(value)); - break; - case "txid": - ((LookupApplicationLogsByID)q).txid(value); - break; - } - break; - case "searchForAssets": - switch (property) { - case "asset-id": - ((SearchForAssets)q).assetId(Long.valueOf(value)); - break; - case "creator": - ((SearchForAssets)q).creator(value); - break; - case "include-all": - ((SearchForAssets)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((SearchForAssets)q).limit(Long.valueOf(value)); - break; - case "name": - ((SearchForAssets)q).name(value); - break; - case "next": - ((SearchForAssets)q).next(value); - break; - case "unit": - ((SearchForAssets)q).unit(value); - break; - } - break; - case "lookupAssetByID": - switch (property) { - case "include-all": - ((LookupAssetByID)q).includeAll(Boolean.valueOf(value)); - break; - } - break; - case "lookupAssetBalances": - switch (property) { - case "currency-greater-than": - ((LookupAssetBalances)q).currencyGreaterThan(Long.valueOf(value)); - break; - case "currency-less-than": - ((LookupAssetBalances)q).currencyLessThan(Long.valueOf(value)); - break; - case "include-all": - ((LookupAssetBalances)q).includeAll(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAssetBalances)q).limit(Long.valueOf(value)); - break; - case "next": - ((LookupAssetBalances)q).next(value); - break; - } - break; - case "lookupAssetTransactions": - switch (property) { - case "address": - ((LookupAssetTransactions)q).address(new Address(value)); - break; - case "address-role": - ((LookupAssetTransactions)q).addressRole(getAddressRole(value)); - break; - case "after-time": - ((LookupAssetTransactions)q).afterTime(Utils.parseDate(value)); - break; - case "before-time": - ((LookupAssetTransactions)q).beforeTime(Utils.parseDate(value)); - break; - case "currency-greater-than": - ((LookupAssetTransactions)q).currencyGreaterThan(Long.valueOf(value)); - break; - case "currency-less-than": - ((LookupAssetTransactions)q).currencyLessThan(Long.valueOf(value)); - break; - case "exclude-close-to": - ((LookupAssetTransactions)q).excludeCloseTo(Boolean.valueOf(value)); - break; - case "limit": - ((LookupAssetTransactions)q).limit(Long.valueOf(value)); - break; - case "max-round": - ((LookupAssetTransactions)q).maxRound(Long.valueOf(value)); - break; - case "min-round": - ((LookupAssetTransactions)q).minRound(Long.valueOf(value)); - break; - case "next": - ((LookupAssetTransactions)q).next(value); - break; - case "note-prefix": - ((LookupAssetTransactions)q).notePrefix(Encoder.decodeFromBase64(value)); - break; - case "rekey-to": - ((LookupAssetTransactions)q).rekeyTo(Boolean.valueOf(value)); - break; - case "round": - ((LookupAssetTransactions)q).round(Long.valueOf(value)); - break; - case "sig-type": - ((LookupAssetTransactions)q).sigType(getSigType(value)); - break; - case "tx-type": - ((LookupAssetTransactions)q).txType(getTxType(value)); - break; - case "txid": - ((LookupAssetTransactions)q).txid(value); - break; - } - break; - case "lookupBlock": - switch (property) { - } - break; - case "lookupTransaction": - switch (property) { - } - break; - case "searchForTransactions": - switch (property) { - case "address": - ((SearchForTransactions)q).address(new Address(value)); - break; - case "address-role": - ((SearchForTransactions)q).addressRole(getAddressRole(value)); - break; - case "after-time": - ((SearchForTransactions)q).afterTime(Utils.parseDate(value)); - break; - case "application-id": - ((SearchForTransactions)q).applicationId(Long.valueOf(value)); - break; - case "asset-id": - ((SearchForTransactions)q).assetId(Long.valueOf(value)); - break; - case "before-time": - ((SearchForTransactions)q).beforeTime(Utils.parseDate(value)); - break; - case "currency-greater-than": - ((SearchForTransactions)q).currencyGreaterThan(Long.valueOf(value)); - break; - case "currency-less-than": - ((SearchForTransactions)q).currencyLessThan(Long.valueOf(value)); - break; - case "exclude-close-to": - ((SearchForTransactions)q).excludeCloseTo(Boolean.valueOf(value)); - break; - case "limit": - ((SearchForTransactions)q).limit(Long.valueOf(value)); - break; - case "max-round": - ((SearchForTransactions)q).maxRound(Long.valueOf(value)); - break; - case "min-round": - ((SearchForTransactions)q).minRound(Long.valueOf(value)); - break; - case "next": - ((SearchForTransactions)q).next(value); - break; - case "note-prefix": - ((SearchForTransactions)q).notePrefix(Encoder.decodeFromBase64(value)); - break; - case "rekey-to": - ((SearchForTransactions)q).rekeyTo(Boolean.valueOf(value)); - break; - case "round": - ((SearchForTransactions)q).round(Long.valueOf(value)); - break; - case "sig-type": - ((SearchForTransactions)q).sigType(getSigType(value)); - break; - case "tx-type": - ((SearchForTransactions)q).txType(getTxType(value)); - break; - case "txid": - ((SearchForTransactions)q).txid(value); - break; - } - break; - case "HealthCheck": - switch (property) { - } - break; - case "Metrics": - switch (property) { - } - break; - case "GetGenesis": - switch (property) { - } - break; - case "SwaggerJSON": - switch (property) { - } - break; - case "GetVersion": - switch (property) { - } - break; - case "AccountInformation": - switch (property) { - case "exclude": - ((AccountInformation)q).exclude(getExclude(value)); - break; - } - break; - case "AccountAssetInformation": - switch (property) { - } - break; - case "AccountApplicationInformation": - switch (property) { - } - break; - case "GetPendingTransactionsByAddress": - switch (property) { - case "max": - ((GetPendingTransactionsByAddress)q).max(Long.valueOf(value)); - break; - } - break; - case "GetBlock": - switch (property) { - } - break; - case "GetProof": - switch (property) { - } - break; - case "GetSupply": - switch (property) { - } - break; - case "GetStatus": - switch (property) { - } - break; - case "WaitForBlock": - switch (property) { - } - break; - case "RawTransaction": - switch (property) { - case "rawtxn": - ((RawTransaction)q).rawtxn(Encoder.decodeFromBase64(value)); - break; - } - break; - case "TransactionParams": - switch (property) { - } - break; - case "GetPendingTransactions": - switch (property) { - case "max": - ((GetPendingTransactions)q).max(Long.valueOf(value)); - break; - } - break; - case "PendingTransactionInformation": - switch (property) { - } - break; - case "GetApplicationByID": - switch (property) { - } - break; - case "GetAssetByID": - switch (property) { - } - break; - case "TealCompile": - switch (property) { - case "source": - ((TealCompile)q).source(Encoder.decodeFromBase64(value)); - break; - } - break; - case "TealDryrun": - switch (property) { - case "request": - ((TealDryrun)q).request(null); - break; - } - break; - - } - } - - public static String lookup(Query q, String className) throws Exception { - Response resp = q.execute(); - if (resp.body() == null) { - throw new RuntimeException(resp.message()); - } - return resp.body().toString(); - } - - public static Enums.AddressRole getAddressRole(String val) { - switch(val.toUpperCase()) { - case "SENDER": - return Enums.AddressRole.SENDER; - case "RECEIVER": - return Enums.AddressRole.RECEIVER; - case "FREEZETARGET": - return Enums.AddressRole.FREEZETARGET; - default: - throw new RuntimeException("Enum value not recognized: " + val +"!"); - } - } - public static Enums.SigType getSigType(String val) { - switch(val.toUpperCase()) { - case "SIG": - return Enums.SigType.SIG; - case "MSIG": - return Enums.SigType.MSIG; - case "LSIG": - return Enums.SigType.LSIG; - default: - throw new RuntimeException("Enum value not recognized: " + val +"!"); - } - } - public static Enums.TxType getTxType(String val) { - switch(val.toUpperCase()) { - case "PAY": - return Enums.TxType.PAY; - case "KEYREG": - return Enums.TxType.KEYREG; - case "ACFG": - return Enums.TxType.ACFG; - case "AXFER": - return Enums.TxType.AXFER; - case "AFRZ": - return Enums.TxType.AFRZ; - case "APPL": - return Enums.TxType.APPL; - default: - throw new RuntimeException("Enum value not recognized: " + val +"!"); - } - } - public static Enums.Exclude getExclude(String val) { - switch(val.toUpperCase().replaceAll("-", "")) { - case "ALL": - return Enums.Exclude.ALL; - case "ASSETS": - return Enums.Exclude.ASSETS; - case "CREATEDASSETS": - return Enums.Exclude.CREATEDASSETS; - case "APPSLOCALSTATE": - return Enums.Exclude.APPSLOCALSTATE; - case "CREATEDAPPS": - return Enums.Exclude.CREATEDAPPS; - case "NONE": - return Enums.Exclude.NONE; - default: - throw new RuntimeException("Enum value not recognized: " + val +"!"); - } - } -} \ No newline at end of file diff --git a/src/test/unit.tags b/src/test/unit.tags new file mode 100644 index 000000000..a3f91cec3 --- /dev/null +++ b/src/test/unit.tags @@ -0,0 +1,29 @@ +@unit.abijson +@unit.abijson.byname +@unit.algod +@unit.algod.ledger_refactoring +@unit.applications +@unit.atomic_transaction_composer +@unit.dryrun +@unit.dryrun.trace.application +@unit.feetest +@unit.indexer +@unit.indexer.ledger_refactoring +@unit.indexer.logs +@unit.indexer.rekey +@unit.offline +@unit.program_sanity_check +@unit.rekey +@unit.responses +@unit.responses.231 +@unit.responses.messagepack +@unit.responses.messagepack.231 +@unit.responses.unlimited_assets +@unit.sourcemap +@unit.stateproof.paths +@unit.stateproof.responses +@unit.stateproof.responses.msgp +@unit.tealsign +@unit.transactions +@unit.transactions.keyreg +@unit.transactions.payment diff --git a/test-harness.sh b/test-harness.sh new file mode 100755 index 000000000..a5d28a781 --- /dev/null +++ b/test-harness.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -euo pipefail + +START=$(date "+%s") + +THIS=$(basename "$0") +ENV_FILE=".test-env" +TEST_DIR="src/test/resources/com/algorand/algosdk" +TEST_RESOURCES_DIR="src/test/resources/" + +set -a +source "$ENV_FILE" +set +a + +rootdir=$(dirname "$0") +pushd "$rootdir" + +echo "$THIS: VERBOSE_HARNESS=$VERBOSE_HARNESS" + +## Reset test harness +if [ -d "$SDK_TESTING_HARNESS" ]; then + pushd "$SDK_TESTING_HARNESS" + ./scripts/down.sh + popd + rm -rf "$SDK_TESTING_HARNESS" +else + echo "$THIS: directory $SDK_TESTING_HARNESS does not exist - NOOP" +fi + +git clone --depth 1 --single-branch --branch "$SDK_TESTING_BRANCH" "$SDK_TESTING_URL" "$SDK_TESTING_HARNESS" + + +echo "$THIS: OVERWRITE_TESTING_ENVIRONMENT=$OVERWRITE_TESTING_ENVIRONMENT" +if [[ $OVERWRITE_TESTING_ENVIRONMENT == 1 ]]; then + echo "$THIS: OVERWRITE replaced $SDK_TESTING_HARNESS/.env with $ENV_FILE:" + cp "$ENV_FILE" "$SDK_TESTING_HARNESS"/.env +fi + +echo "$THIS: REMOVE_LOCAL_FEATURES=$REMOVE_LOCAL_FEATURES"## Copy feature files into the project resources +if [[ $REMOVE_LOCAL_FEATURES == 1 ]]; then + echo "$THIS: OVERWRITE wipes clean $TEST_DIR/features" + if [[ $VERBOSE_HARNESS == 1 ]]; then + ( tree $TEST_DIR/integration && tree $TEST_DIR/unit && tree $TEST_RESOURCES_DIR && echo "$THIS: see the previous for files deleted" ) || true + fi + rm -rf $TEST_DIR/integration + rm -rf $TEST_DIR/unit + rm -rf $TEST_RESOURCES_DIR +fi +mkdir -p $TEST_DIR/integration +mkdir -p $TEST_DIR/unit +mkdir -p $TEST_RESOURCES_DIR + +# The Java implementation of these is too tightly coupled with the +# integration tests, so add them to the integration tests instead. +mv "$SDK_TESTING_HARNESS"/features/unit/offline.feature "$SDK_TESTING_HARNESS"/features/integration/ + +cp -r "$SDK_TESTING_HARNESS"/features/integration/* $TEST_DIR/integration +cp -r "$SDK_TESTING_HARNESS"/features/unit/* $TEST_DIR/unit +cp -r "$SDK_TESTING_HARNESS"/features/resources/* $TEST_RESOURCES_DIR + +if [[ $VERBOSE_HARNESS == 1 ]]; then + ( tree $TEST_DIR/integration && tree $TEST_DIR/unit && tree $TEST_RESOURCES_DIR && echo "$THIS: see the previous for files copied over" ) || true +fi +echo "$THIS: seconds it took to get to end of cloning and copying: $(($(date "+%s") - START))s" + +## Start test harness environment +pushd "$SDK_TESTING_HARNESS" + +[[ "$VERBOSE_HARNESS" = 1 ]] && V_FLAG="-v" || V_FLAG="" +echo "$THIS: standing up harnness with command [./up.sh $V_FLAG]" +./scripts/up.sh "$V_FLAG" + +popd +echo "$THIS: seconds it took to finish testing sdk's up.sh: $(($(date "+%s") - START))s" +echo "" +echo "--------------------------------------------------------------------------------" +echo "|" +echo "| To run sandbox commands, cd into $SDK_TESTING_HARNESS/.sandbox " +echo "|" +echo "--------------------------------------------------------------------------------"