diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml new file mode 100644 index 000000000..2f906361d --- /dev/null +++ b/.github/workflows/create-release-pr.yml @@ -0,0 +1,230 @@ +name: Create Release PR + +on: + workflow_dispatch: + inputs: + release_version: + description: 'The release_version used for the release branch name, e.g. release/x.x.x' + default: 'x.x.x' + required: true + type: string + pre_release_version: + description: "Pre-Release version, e.g. 'beta-1'" + required: false + type: string + +env: + RELEASE_VERSION: ${{ inputs.release_version }} + PRE_RELEASE_VERSION: ${{ inputs.pre_release_version }} + RELEASE_BRANCH: release/${{ inputs.release_version }} + +jobs: + create-release-pr: + runs-on: ubuntu-latest + + steps: + - name: Set Release Version and Branch to Check Out + id: set-release + run: | + if [[ $RELEASE_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ $PRE_RELEASE_VERSION =~ ^[-a-z0-9]+$ ]]; then + echo "release-tag: $RELEASE_VERSION-$PRE_RELEASE_VERSION" + echo "release-tag=$RELEASE_VERSION-$PRE_RELEASE_VERSION" >> $GITHUB_OUTPUT + elif [[ -n $PRE_RELEASE_VERSION ]]; then + echo "Input pre_release_version is not empty, but does not match the regex pattern ^[-a-z0-9]+$" + exit 1 + else + echo "release-tag: $RELEASE_VERSION" + echo "release-tag=$RELEASE_VERSION" >> $GITHUB_OUTPUT + fi + else + echo "Version input doesn't match the regex pattern ^[0-9]+\.[0-9]+\.[0-9]+$" + exit 1 + fi + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Create Release Branch if it does not exist + run: | + if ! git show-ref --verify --quiet "refs/remotes/origin/$RELEASE_BRANCH"; then + git checkout -b $RELEASE_BRANCH + git push --set-upstream origin $RELEASE_BRANCH + elif [[ $(git rev-parse --abbrev-ref HEAD) != "$RELEASE_BRANCH" ]]; then + echo "Current Branch: $(git rev-parse --abbrev-ref HEAD)" + echo "Release branch exists, make sure you're using the workflow from the release branch or delete the existing release branch." + exit 1 + else + echo "Release branch exists and used as workflow ref." + fi + + - name: Get Latest Release + id: get-release + run: | + if [[ -n $PRE_RELEASE_VERSION ]]; then + echo "Get the latest release" + tag=$(curl -L \ + --header "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/releases" | jq -r '.[0].tag_name') + echo "latest-tag=$tag" >> $GITHUB_OUTPUT + else + echo "Get the latest stable release" + tag=$(curl -L \ + --header "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r '.tag_name') + echo "latest-tag=$tag" >> $GITHUB_OUTPUT + fi + + - name: Build Changelog + uses: mikepenz/release-changelog-builder-action@v3.7.2 + id: build-changelog + env: + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + with: + fromTag: ${{ env.PREVIOUS_VERSION }} + toTag: ${{ env.RELEASE_BRANCH }} + failOnError: true + configurationJson: | + { + "categories": [ + { + "title": "## New Features", + "labels": [ + "New Feature" + ] + }, + { + "title": "## Enhancements", + "labels": [ + "Enhancement" + ] + }, + { + "title": "## Bug Fixes", + "labels": [ + "Bug-Fix" + ] + }, + { + "title": "## Not Yet Enabled", + "labels": [ + "Not-Yet-Enabled" + ] + } + ], + "ignore_labels": [ + "Skip-Release-Notes" + ], + "sort": { + "order": "ASC", + "on_property": "mergedAt" + }, + "template": "#{{CHANGELOG}}", + "pr_template": "- #{{TITLE}} by @#{{AUTHOR}} in ##{{NUMBER}}" + } + + - name: Update Changelog + if: ${{ env.PRE_RELEASE_VERSION == '' }} + env: + CHANGELOG_CONTENT: ${{ steps.build-changelog.outputs.changelog }} + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + run: | + echo -e "# ${RELEASE_VERSION}\n\n${CHANGELOG_CONTENT}**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_VERSION}...${RELEASE_VERSION}\n" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md + + - name: Update Version References in Source + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + python3 scripts/bump_version.py ${RELEASE_TAG} + + - name: Commit Changes + uses: EndBug/add-and-commit@v9.1.3 + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + with: + message: "bump up version to ${{ env.RELEASE_TAG }}" + + - name: Create Pull Request to Master + env: + CHANGELOG_CONTENT: ${{ steps.build-changelog.outputs.changelog }} + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + echo -e "# What's Changed\n\n${CHANGELOG_CONTENT}**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_VERSION}...${RELEASE_TAG}" > tmp_msg_body.txt + export msg_body=$(cat tmp_msg_body.txt) + rm tmp_msg_body.txt + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 + PULL_REQUEST_URL=$(gh pr create --base "master" \ + --title "FOR REVIEW ONLY: ${{ github.event.repository.name }} $RELEASE_TAG" \ + --label "Skip-Release-Notes" \ + --label "Team Hyper Flow" \ + --body "$msg_body" | tail -n 1) + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + PULL_REQUEST_NUM=$(echo $PULL_REQUEST_URL | sed 's:.*/::') + echo "pull-request-master=$PULL_REQUEST_URL" >> $GITHUB_ENV + echo "pull-request-master-num=$PULL_REQUEST_NUM" >> $GITHUB_ENV + echo "Pull request to Master created: $PULL_REQUEST_URL" + else + echo "There was an issue creating the pull request to master branch." + exit 1 + fi + + - name: Create Pull Request to Develop + if: ${{ env.PRE_RELEASE_VERSION == '' }} + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 + PULL_REQUEST_URL=$(gh pr create --base "develop" \ + --title "FOR REVIEW ONLY: Merge back ${{ github.event.repository.name }} $RELEASE_TAG to develop" \ + --label "Skip-Release-Notes" \ + --label "Team Hyper Flow" \ + --body "Merge back version changes to develop." | tail -n 1) + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + echo "Pull request to Develop created: $PULL_REQUEST_URL" + DEVELOP_PR_MESSAGE="\nPull Request to develop: $PULL_REQUEST_URL" + echo "pull-request-develop-message=$DEVELOP_PR_MESSAGE" >> $GITHUB_ENV + else + echo "There was an issue creating the pull request to develop branch." + exit 1 + fi + + - name: Send Slack Message + id: slack + uses: slackapi/slack-github-action@v1.24.0 + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + SDK_DEPLOYMENT_URL: ${{ secrets.SDK_DEPLOYMENT_URL }} + with: + payload: | + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ github.event.repository.name }} Release PR for ${{ env.RELEASE_TAG }}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Approvals needed for*:\nPull Request to master: ${{ env.pull-request-master}}${{ env.pull-request-develop-message }}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*After approvals*\nDeploy SDK using the <${{ env.SDK_DEPLOYMENT_URL }}|Deployment Pipeline> with the following parameters:\n*SDK*: ${{ github.event.repository.name }}\n*RELEASE_PR_NUM*: ${{ env.pull-request-master-num }}\n*RELEASE_VERSION*: ${{ env.RELEASE_VERSION }}\n*PRE_RELEASE_VERSION*: ${{ env.PRE_RELEASE_VERSION }}" + } + } + ] + } diff --git a/.test-env b/.test-env index df783a4fd..049289d7b 100644 --- a/.test-env +++ b/.test-env @@ -5,7 +5,7 @@ SDK_TESTING_HARNESS="test-harness" INSTALL_ONLY=0 -VERBOSE_HARNESS=0 +VERBOSE_HARNESS=1 # WARNING: If set to 1, new features will be LOST when downloading the test harness. # REGARDLESS: modified features are ALWAYS overwritten. diff --git a/CHANGELOG.md b/CHANGELOG.md index ae86c0cf8..b0bae6a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 2.2.0 + +## Enhancements + +- DevOps: Update CODEOWNERS to only refer to the devops group by @onetechnical in #571 +- deltas: Deltas apis by @Eric-Warehime in #575 +- algod: Regen changes for Deltas endpoints by @Eric-Warehime in #590 + +## Bug Fixes + +- docs: remove old example from README by @winder in #561 + +**Full Changelog**: https://github.com/algorand/java-algorand-sdk/compare/2.1.0...2.2.0 + # 2.1.0 ## What's Changed diff --git a/CODEOWNERS b/CODEOWNERS index aa26c82a4..3c88c6e71 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,2 @@ -.github/ @algorand/dev -.circleci/ @algorand/dev +.github/ @algorand/devops +.circleci/ @algorand/devops diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..966aa2cf9 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +conduct@algorand.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index b25484d73..565d46a31 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Maven: com.algorand algosdk - 2.1.0 + 2.2.0 ``` @@ -198,13 +198,6 @@ This project uses Maven. ~$ mvn package ``` -**To run the example project** -Use the following command in the examples directory, be sure to update your algod network address and the API token -parameters (see examples/README for more information): -``` -~$ mvn exec:java -Dexec.mainClass="com.algorand.algosdk.example.Main" -Dexec.args="127.0.0.1:8080 ***X-Algo-API-Token***" -``` - ### **To test** We are using separate version targets for production and testing to allow using JUnit5 for tests. Some IDEs, like IDEA do not support this very well. To workaround the issue a special `ide` profile should be enabled if your IDE does not diff --git a/pom.xml b/pom.xml index ec8d71f2b..adadd9098 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.algorand algosdk - 2.1.0 + 2.2.0 jar ${project.groupId}:${project.artifactId} diff --git a/scripts/bump_version.py b/scripts/bump_version.py new file mode 100755 index 000000000..7af8f6aa0 --- /dev/null +++ b/scripts/bump_version.py @@ -0,0 +1,38 @@ +# This script bumps up the version in `pom.xml` and `README.md` for new releases. +# Usage: python bump_version.py {new_version} (--read_me --pom_xml ) + +import argparse +import re + +def bump_version(new_version, file_path): + with open(file_path, "r") as file: + content = file.read() + + # Replace first instance of + new_content = re.sub( + '[0-9]+\.[0-9]+\.[-a-z0-9]+', + f'{new_version}', + content, 1 + ) + + with open(file_path, "w") as file: + file.write(new_content) + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="updates the version for a release", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("new_version", help="New Version as major.minor.patch") + parser.add_argument( + "--read_me", default="README.md", help="path to README.md" + ) + parser.add_argument( + "--pom_xml", default="pom.xml", help="path to pom.xml" + ) + + args = parser.parse_args() + + bump_version(args.new_version, args.read_me) + bump_version(args.new_version, args.pom_xml) + diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetApplicationBoxByName.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetApplicationBoxByName.java index 2eab16704..61b6a079d 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/algod/GetApplicationBoxByName.java +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetApplicationBoxByName.java @@ -9,8 +9,8 @@ /** - * Given an application ID and box name, it returns the box name and value (each - * base64 encoded). Box names must be in the goal app call arg encoding form + * Given an application ID and box name, it returns the round, box name, and value + * (each base64 encoded). Box names must be in the goal app call arg encoding form * 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, use the form * 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use * the form 'addr:XYZ...'. diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDelta.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDelta.java new file mode 100644 index 000000000..8bd624ac0 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDelta.java @@ -0,0 +1,65 @@ +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.LedgerStateDelta; + + +/** + * Get ledger deltas for a round. + * /v2/deltas/{round} + */ +public class GetLedgerStateDelta extends Query { + + private Long round; + + /** + * @param round The round for which the deltas are desired. + */ + public GetLedgerStateDelta(Client client, Long round) { + super(client, new HttpMethod("get")); + addQuery("format", "msgpack"); + this.round = round; + } + + /** + * Execute the query. + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(LedgerStateDelta.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(LedgerStateDelta.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("deltas")); + addPathSegment(String.valueOf(round)); + + return qd; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDeltaForTransactionGroup.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDeltaForTransactionGroup.java new file mode 100644 index 000000000..2f47e1b9e --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetLedgerStateDeltaForTransactionGroup.java @@ -0,0 +1,67 @@ +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.LedgerStateDelta; + + +/** + * Get a ledger delta for a given transaction group. + * /v2/deltas/txn/group/{id} + */ +public class GetLedgerStateDeltaForTransactionGroup extends Query { + + private String id; + + /** + * @param id A transaction ID, or transaction group ID + */ + public GetLedgerStateDeltaForTransactionGroup(Client client, String id) { + super(client, new HttpMethod("get")); + addQuery("format", "msgpack"); + this.id = id; + } + + /** + * Execute the query. + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(LedgerStateDelta.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(LedgerStateDelta.class); + return resp; + } + + protected QueryData getRequestString() { + if (this.id == null) { + throw new RuntimeException("id is not set. It is a required parameter."); + } + addPathSegment(String.valueOf("v2")); + addPathSegment(String.valueOf("deltas")); + addPathSegment(String.valueOf("txn")); + addPathSegment(String.valueOf("group")); + addPathSegment(String.valueOf(id)); + + return qd; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionGroupLedgerStateDeltasForRound.java b/src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionGroupLedgerStateDeltasForRound.java new file mode 100644 index 000000000..204e72f35 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/algod/GetTransactionGroupLedgerStateDeltasForRound.java @@ -0,0 +1,67 @@ +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.TransactionGroupLedgerStateDeltasForRoundResponse; + + +/** + * Get ledger deltas for transaction groups in a given round. + * /v2/deltas/{round}/txn/group + */ +public class GetTransactionGroupLedgerStateDeltasForRound extends Query { + + private Long round; + + /** + * @param round The round for which the deltas are desired. + */ + public GetTransactionGroupLedgerStateDeltasForRound(Client client, Long round) { + super(client, new HttpMethod("get")); + addQuery("format", "msgpack"); + this.round = round; + } + + /** + * Execute the query. + * @return the query response object. + * @throws Exception + */ + @Override + public Response execute() throws Exception { + Response resp = baseExecute(); + resp.setValueType(TransactionGroupLedgerStateDeltasForRoundResponse.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(TransactionGroupLedgerStateDeltasForRoundResponse.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("deltas")); + addPathSegment(String.valueOf(round)); + addPathSegment(String.valueOf("txn")); + addPathSegment(String.valueOf("group")); + + return qd; + } +} 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 84fd4fd29..08f90299c 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 @@ -21,6 +21,9 @@ 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.GetLedgerStateDelta; +import com.algorand.algosdk.v2.client.algod.GetTransactionGroupLedgerStateDeltasForRound; +import com.algorand.algosdk.v2.client.algod.GetLedgerStateDeltaForTransactionGroup; import com.algorand.algosdk.v2.client.algod.GetStateProof; import com.algorand.algosdk.v2.client.algod.GetLightBlockHeaderProof; import com.algorand.algosdk.v2.client.algod.GetApplicationByID; @@ -251,6 +254,30 @@ public PendingTransactionInformation PendingTransactionInformation(String txid) return new PendingTransactionInformation((Client) this, txid); } + /** + * Get ledger deltas for a round. + * /v2/deltas/{round} + */ + public GetLedgerStateDelta GetLedgerStateDelta(Long round) { + return new GetLedgerStateDelta((Client) this, round); + } + + /** + * Get ledger deltas for transaction groups in a given round. + * /v2/deltas/{round}/txn/group + */ + public GetTransactionGroupLedgerStateDeltasForRound GetTransactionGroupLedgerStateDeltasForRound(Long round) { + return new GetTransactionGroupLedgerStateDeltasForRound((Client) this, round); + } + + /** + * Get a ledger delta for a given transaction group. + * /v2/deltas/txn/group/{id} + */ + public GetLedgerStateDeltaForTransactionGroup GetLedgerStateDeltaForTransactionGroup(String id) { + return new GetLedgerStateDeltaForTransactionGroup((Client) this, id); + } + /** * Get a state proof that covers a given round * /v2/stateproofs/{round} @@ -287,8 +314,8 @@ public GetApplicationBoxes GetApplicationBoxes(Long applicationId) { } /** - * Given an application ID and box name, it returns the box name and value (each - * base64 encoded). Box names must be in the goal app call arg encoding form + * Given an application ID and box name, it returns the round, box name, and value + * (each base64 encoded). Box names must be in the goal app call arg encoding form * 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, use the form * 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use * the form 'addr:XYZ...'. diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/Box.java b/src/main/java/com/algorand/algosdk/v2/client/model/Box.java index 326593c4a..3498ceedf 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/Box.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/Box.java @@ -23,6 +23,12 @@ public String name() { } public byte[] name; + /** + * The round for which this information is relevant + */ + @JsonProperty("round") + public Long round; + /** * (value) box value, base64 encoded. */ @@ -43,6 +49,7 @@ public boolean equals(Object o) { Box other = (Box) o; if (!Objects.deepEquals(this.name, other.name)) return false; + if (!Objects.deepEquals(this.round, other.round)) return false; if (!Objects.deepEquals(this.value, other.value)) return false; return true; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDelta.java b/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDelta.java new file mode 100644 index 000000000..2f14c35f9 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDelta.java @@ -0,0 +1,8 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.HashMap; + +/** + * Contains a ledger delta + */ +public class LedgerStateDelta extends HashMap {} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDeltaForTransactionGroup.java b/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDeltaForTransactionGroup.java new file mode 100644 index 000000000..cb518baab --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/LedgerStateDeltaForTransactionGroup.java @@ -0,0 +1,37 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Contains a ledger delta for a single transaction group + */ +public class LedgerStateDeltaForTransactionGroup extends PathResponse { + + /** + * Ledger StateDelta object + */ + @JsonProperty("Delta") + public HashMap delta; + + @JsonProperty("Ids") + public List ids = new ArrayList(); + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + LedgerStateDeltaForTransactionGroup other = (LedgerStateDeltaForTransactionGroup) o; + if (!Objects.deepEquals(this.delta, other.delta)) return false; + if (!Objects.deepEquals(this.ids, other.ids)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateRequest.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateRequest.java index d11447cc5..68b69bed1 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateRequest.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateRequest.java @@ -25,6 +25,18 @@ public class SimulateRequest extends PathResponse { @JsonProperty("allow-more-logging") public Boolean allowMoreLogging; + /** + * An object that configures simulation execution trace. + */ + @JsonProperty("exec-trace-config") + public SimulateTraceConfig execTraceConfig; + + /** + * Applies extra opcode budget during simulation for each transaction group. + */ + @JsonProperty("extra-opcode-budget") + public Long extraOpcodeBudget; + /** * The transaction groups to simulate. */ @@ -40,6 +52,8 @@ public boolean equals(Object o) { SimulateRequest other = (SimulateRequest) o; if (!Objects.deepEquals(this.allowEmptySignatures, other.allowEmptySignatures)) return false; if (!Objects.deepEquals(this.allowMoreLogging, other.allowMoreLogging)) return false; + if (!Objects.deepEquals(this.execTraceConfig, other.execTraceConfig)) return false; + if (!Objects.deepEquals(this.extraOpcodeBudget, other.extraOpcodeBudget)) return false; if (!Objects.deepEquals(this.txnGroups, other.txnGroups)) return false; return true; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateResponse.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateResponse.java index 213713d35..0dad12b91 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateResponse.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateResponse.java @@ -20,6 +20,12 @@ public class SimulateResponse extends PathResponse { @JsonProperty("eval-overrides") public SimulationEvalOverrides evalOverrides; + /** + * An object that configures simulation execution trace. + */ + @JsonProperty("exec-trace-config") + public SimulateTraceConfig execTraceConfig; + /** * The round immediately preceding this simulation. State changes through this * round were used to run this simulation. @@ -47,6 +53,7 @@ public boolean equals(Object o) { SimulateResponse other = (SimulateResponse) o; if (!Objects.deepEquals(this.evalOverrides, other.evalOverrides)) return false; + if (!Objects.deepEquals(this.execTraceConfig, other.execTraceConfig)) return false; if (!Objects.deepEquals(this.lastRound, other.lastRound)) return false; if (!Objects.deepEquals(this.txnGroups, other.txnGroups)) return false; if (!Objects.deepEquals(this.version, other.version)) return false; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTraceConfig.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTraceConfig.java new file mode 100644 index 000000000..9e7266810 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTraceConfig.java @@ -0,0 +1,30 @@ +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; + +/** + * An object that configures simulation execution trace. + */ +public class SimulateTraceConfig extends PathResponse { + + /** + * A boolean option for opting in execution trace features simulation endpoint. + */ + @JsonProperty("enable") + public Boolean enable; + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + SimulateTraceConfig other = (SimulateTraceConfig) o; + if (!Objects.deepEquals(this.enable, other.enable)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTransactionResult.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTransactionResult.java index 783cb6435..280084656 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTransactionResult.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulateTransactionResult.java @@ -17,6 +17,13 @@ public class SimulateTransactionResult extends PathResponse { @JsonProperty("app-budget-consumed") public Long appBudgetConsumed; + /** + * The execution trace of calling an app or a logic sig, containing the inner app + * call trace in a recursive way. + */ + @JsonProperty("exec-trace") + public SimulationTransactionExecTrace execTrace; + /** * Budget used during execution of a logic sig transaction. */ @@ -38,6 +45,7 @@ public boolean equals(Object o) { SimulateTransactionResult other = (SimulateTransactionResult) o; if (!Objects.deepEquals(this.appBudgetConsumed, other.appBudgetConsumed)) return false; + if (!Objects.deepEquals(this.execTrace, other.execTrace)) return false; if (!Objects.deepEquals(this.logicSigBudgetConsumed, other.logicSigBudgetConsumed)) return false; if (!Objects.deepEquals(this.txnResult, other.txnResult)) return false; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulationEvalOverrides.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationEvalOverrides.java index 7737596b6..26b533b53 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/SimulationEvalOverrides.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationEvalOverrides.java @@ -19,17 +19,23 @@ public class SimulationEvalOverrides extends PathResponse { @JsonProperty("allow-empty-signatures") public Boolean allowEmptySignatures; + /** + * The extra opcode budget added to each transaction group during simulation + */ + @JsonProperty("extra-opcode-budget") + public Long extraOpcodeBudget; + /** * The maximum log calls one can make during simulation */ @JsonProperty("max-log-calls") - public java.math.BigInteger maxLogCalls; + public Long maxLogCalls; /** * The maximum byte number to log during simulation */ @JsonProperty("max-log-size") - public java.math.BigInteger maxLogSize; + public Long maxLogSize; @Override public boolean equals(Object o) { @@ -39,6 +45,7 @@ public boolean equals(Object o) { SimulationEvalOverrides other = (SimulationEvalOverrides) o; if (!Objects.deepEquals(this.allowEmptySignatures, other.allowEmptySignatures)) return false; + if (!Objects.deepEquals(this.extraOpcodeBudget, other.extraOpcodeBudget)) return false; if (!Objects.deepEquals(this.maxLogCalls, other.maxLogCalls)) return false; if (!Objects.deepEquals(this.maxLogSize, other.maxLogSize)) return false; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulationOpcodeTraceUnit.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationOpcodeTraceUnit.java new file mode 100644 index 000000000..609b808cd --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationOpcodeTraceUnit.java @@ -0,0 +1,39 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The set of trace information and effect from evaluating a single opcode. + */ +public class SimulationOpcodeTraceUnit extends PathResponse { + + /** + * The program counter of the current opcode being evaluated. + */ + @JsonProperty("pc") + public Long pc; + + /** + * The indexes of the traces for inner transactions spawned by this opcode, if any. + */ + @JsonProperty("spawned-inners") + public List spawnedInners = new ArrayList(); + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + SimulationOpcodeTraceUnit other = (SimulationOpcodeTraceUnit) o; + if (!Objects.deepEquals(this.pc, other.pc)) return false; + if (!Objects.deepEquals(this.spawnedInners, other.spawnedInners)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/SimulationTransactionExecTrace.java b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationTransactionExecTrace.java new file mode 100644 index 000000000..71219959d --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/SimulationTransactionExecTrace.java @@ -0,0 +1,55 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The execution trace of calling an app or a logic sig, containing the inner app + * call trace in a recursive way. + */ +public class SimulationTransactionExecTrace extends PathResponse { + + /** + * Program trace that contains a trace of opcode effects in an approval program. + */ + @JsonProperty("approval-program-trace") + public List approvalProgramTrace = new ArrayList(); + + /** + * Program trace that contains a trace of opcode effects in a clear state program. + */ + @JsonProperty("clear-state-program-trace") + public List clearStateProgramTrace = new ArrayList(); + + /** + * An array of SimulationTransactionExecTrace representing the execution trace of + * any inner transactions executed. + */ + @JsonProperty("inner-trace") + public List innerTrace = new ArrayList(); + + /** + * Program trace that contains a trace of opcode effects in a logic sig. + */ + @JsonProperty("logic-sig-trace") + public List logicSigTrace = new ArrayList(); + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + SimulationTransactionExecTrace other = (SimulationTransactionExecTrace) o; + if (!Objects.deepEquals(this.approvalProgramTrace, other.approvalProgramTrace)) return false; + if (!Objects.deepEquals(this.clearStateProgramTrace, other.clearStateProgramTrace)) return false; + if (!Objects.deepEquals(this.innerTrace, other.innerTrace)) return false; + if (!Objects.deepEquals(this.logicSigTrace, other.logicSigTrace)) return false; + + return true; + } +} diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/TransactionAssetTransfer.java b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionAssetTransfer.java index 5480bf5b1..003b26c67 100644 --- a/src/main/java/com/algorand/algosdk/v2/client/model/TransactionAssetTransfer.java +++ b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionAssetTransfer.java @@ -26,7 +26,7 @@ public class TransactionAssetTransfer extends PathResponse { public Long assetId; /** - * Number of assets transfered to the close-to account as part of the transaction. + * Number of assets transferred to the close-to account as part of the transaction. */ @JsonProperty("close-amount") public java.math.BigInteger closeAmount; diff --git a/src/main/java/com/algorand/algosdk/v2/client/model/TransactionGroupLedgerStateDeltasForRoundResponse.java b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionGroupLedgerStateDeltasForRoundResponse.java new file mode 100644 index 000000000..4d4cf6596 --- /dev/null +++ b/src/main/java/com/algorand/algosdk/v2/client/model/TransactionGroupLedgerStateDeltasForRoundResponse.java @@ -0,0 +1,30 @@ +package com.algorand.algosdk.v2.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.algorand.algosdk.v2.client.common.PathResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Response containing all ledger state deltas for transaction groups, with their + * associated Ids, in a single round. + */ +public class TransactionGroupLedgerStateDeltasForRoundResponse extends PathResponse { + + @JsonProperty("Deltas") + public List deltas = new ArrayList(); + + @Override + public boolean equals(Object o) { + + if (this == o) return true; + if (o == null) return false; + + TransactionGroupLedgerStateDeltasForRoundResponse other = (TransactionGroupLedgerStateDeltasForRoundResponse) o; + if (!Objects.deepEquals(this.deltas, other.deltas)) return false; + + return true; + } +} diff --git a/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java b/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java index d3cae8855..2acfb34d9 100644 --- a/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java +++ b/src/test/java/com/algorand/algosdk/unit/AlgodPaths.java @@ -151,4 +151,19 @@ public void getBlockTimestampOffset() { public void setBlockTimestampOffset(Long round) { ps.q = algodClient.SetBlockTimeStampOffset(round); } + + @When("we make a GetLedgerStateDelta call against round {long}") + public void we_make_a_get_ledger_state_delta_call_against_round(Long round) { + ps.q = algodClient.GetLedgerStateDelta(round); + } + + @When("we make a LedgerStateDeltaForTransactionGroupResponse call for ID {string}") + public void we_make_a_ledger_state_delta_for_transaction_group_response_call_for_id(String id) { + ps.q = algodClient.GetLedgerStateDeltaForTransactionGroup(id); + } + + @When("we make a TransactionGroupLedgerStateDeltaForRoundResponse call for round {long}") + public void we_make_a_transaction_group_ledger_state_delta_for_round_response_call_for_round(Long round) { + ps.q = algodClient.GetTransactionGroupLedgerStateDeltasForRound(round); + } } diff --git a/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java b/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java index 40ce9e9bb..1b208117e 100644 --- a/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java +++ b/src/test/java/com/algorand/algosdk/unit/ResponsesShared.java @@ -200,6 +200,15 @@ public void we_make_any_call_to(String client, String endpoint) throws Exception case "GetSyncRound": response = algod.GetSyncRound().execute(); break; + case "GetTransactionGroupLedgerStateDeltaForRound": + response = algod.GetTransactionGroupLedgerStateDeltasForRound(1234L).execute(); + break; + case "GetLedgerStateDeltaForTransactionGroup": + response = algod.GetLedgerStateDeltaForTransactionGroup("abc123").execute(); + break; + case "GetLedgerStateDelta": + response = algod.GetLedgerStateDelta(1234L).execute(); + break; default: Assertions.fail("Unsupported algod endpoint: " + endpoint); } diff --git a/src/test/unit.tags b/src/test/unit.tags index 720e9743e..820f7edf2 100644 --- a/src/test/unit.tags +++ b/src/test/unit.tags @@ -22,10 +22,13 @@ @unit.responses.blocksummary @unit.responses.messagepack @unit.responses.messagepack.231 +@unit.responses.statedelta @unit.responses.timestamp +@unit.responses.txngroupdeltas @unit.responses.sync @unit.responses.unlimited_assets @unit.sourcemap +@unit.statedelta @unit.stateproof.paths @unit.stateproof.responses @unit.stateproof.responses.msgp @@ -35,3 +38,4 @@ @unit.transactions @unit.transactions.keyreg @unit.transactions.payment +@unit.txngroupdeltas \ No newline at end of file