Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StateProofs: Add State Proof support. #360

Merged
merged 9 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Comment on lines +169 to +173
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These types were really complicated, do they need to be added?


/**
* 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,
id-ms marked this conversation as resolved.
Show resolved Hide resolved
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