Skip to content

Commit

Permalink
StateProofs: Add State Proof support. (#360)
Browse files Browse the repository at this point in the history
* Regenerate client.

* Implement cucumber response tests.

* Implement path tests, update Makefile, update README.

* Revert

* Add state proof fields to Transaction.

* Update README.md

* Update src/main/java/com/algorand/algosdk/transaction/Transaction.java

* publish results.

* Add transaction-root-256
  • Loading branch information
winder authored Aug 30, 2022
1 parent d9b2699 commit be204c1
Show file tree
Hide file tree
Showing 31 changed files with 983 additions and 780 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,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 <branch here>"
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
Expand All @@ -252,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]
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/com/algorand/algosdk/transaction/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String,Object> stateProof = null;

@JsonProperty("spmsg")
public Map<String,Object> stateProofMessage = null;

/**
* Create a payment transaction
* @param fromAddr source address
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -1250,7 +1260,8 @@ public enum Type {
AssetConfig("acfg"),
AssetTransfer("axfer"),
AssetFreeze("afrz"),
ApplicationCall("appl");
ApplicationCall("appl"),
StateProof("stpf");

private static Map<String, Type> namesMap = new HashMap<String, Type>(6);

Expand Down
Original file line number Diff line number Diff line change
@@ -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<LightBlockHeaderProof> execute() throws Exception {
Response<LightBlockHeaderProof> 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<LightBlockHeaderProof> execute(String[] headers, String[] values) throws Exception {
Response<LightBlockHeaderProof> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<StateProof> execute() throws Exception {
Response<StateProof> 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<StateProof> execute(String[] headers, String[] values) throws Exception {
Response<StateProof> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -45,9 +45,9 @@ public GetProof hashtype(Enums.Hashtype hashtype) {
* @throws Exception
*/
@Override
public Response<ProofResponse> execute() throws Exception {
Response<ProofResponse> resp = baseExecute();
resp.setValueType(ProofResponse.class);
public Response<TransactionProofResponse> execute() throws Exception {
Response<TransactionProofResponse> resp = baseExecute();
resp.setValueType(TransactionProofResponse.class);
return resp;
}

Expand All @@ -60,9 +60,9 @@ public Response<ProofResponse> execute() throws Exception {
* @throws Exception
*/
@Override
public Response<ProofResponse> execute(String[] headers, String[] values) throws Exception {
Response<ProofResponse> resp = baseExecute(headers, values);
resp.setValueType(ProofResponse.class);
public Response<TransactionProofResponse> execute(String[] headers, String[] values) throws Exception {
Response<TransactionProofResponse> resp = baseExecute(headers, values);
resp.setValueType(TransactionProofResponse.class);
return resp;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
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;
import com.algorand.algosdk.v2.client.algod.RawTransaction;
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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/algorand/algosdk/v2/client/model/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public String seed() {
}
public byte[] seed;

/**
* Tracks the status of state proofs.
*/
@JsonProperty("state-proof-tracking")
public List<StateProofTracking> stateProofTracking = new ArrayList<StateProofTracking>();

/**
* (ts) Block creation timestamp in seconds since eposh
*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/algorand/algosdk/v2/client/model/Enums.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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;
Expand Down
Loading

0 comments on commit be204c1

Please sign in to comment.