Skip to content

Commit 88d88b0

Browse files
committed
Merge branch 'release/1.11.0-beta-1'
2 parents cda8a83 + d398d0f commit 88d88b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4088
-196
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# 1.11.0-beta-1
2+
- ABI Interaction Support for JAVA SDK (#268)
3+
- Bug fix for `logs` on `PendingTransactionResponse` (#275)
4+
- Add WaitForConfirmation function (#274)
5+
- Better error message on encoding exception. (#258)
6+
- Revert "Revert "Fix ABI source code position for ABI feature (#260)""
7+
- Revert "Revert "Add ABI encoding support (#255)""
8+
19
# 1.10.0
210
- Feature/sign rekey lsig msig (#250)
311
- Support parsing msgpack with numeric key (#262)

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
unit:
2-
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.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"
2+
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.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.atomic_transaction_composer or @unit.transactions.payment"
33

44
integration:
5-
mvn test -Dcucumber.filter.tags="@algod or @assets or @auction or @kmd or @send or @template or @indexer or @rekey or @applications.verified or @applications or @compile or @dryrun or @indexer.applications or @applications.evaldelta or @indexer.231"
5+
mvn test -Dcucumber.filter.tags="@algod or @assets or @auction or @kmd or @send or @template or @indexer or @rekey or @applications.verified or @applications or @compile or @dryrun or @indexer.applications or @applications.evaldelta or @indexer.231 or @abi"
66

77
docker-test:
88
./run_integration_tests.sh

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Maven:
2323
<dependency>
2424
<groupId>com.algorand</groupId>
2525
<artifactId>algosdk</artifactId>
26-
<version>1.10.0</version>
26+
<version>1.11.0-beta-1</version>
2727
</dependency>
2828
```
2929

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.algorand</groupId>
66
<artifactId>algosdk</artifactId>
7-
<version>1.10.0</version>
7+
<version>1.11.0-beta-1</version>
88
<packaging>jar</packaging>
99

1010
<name>${project.groupId}:${project.artifactId}</name>
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package com.algorand.algosdk.abi;
2+
3+
import java.lang.reflect.Array;
4+
import java.util.*;
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
public abstract class ABIType {
9+
public static final int ABI_DYNAMIC_HEAD_BYTE_LEN = 2;
10+
private static final Pattern staticArrayPattern = Pattern.compile("^(?<elemT>[a-z\\d\\[\\](),]+)\\[(?<len>[1-9][\\d]*)]$");
11+
private static final Pattern ufixedPattern = Pattern.compile("^ufixed(?<size>[1-9][\\d]*)x(?<precision>[1-9][\\d]*)$");
12+
13+
/**
14+
* Check if this ABI type is a dynamic type.
15+
* @return boolean decision if the ABI type is dynamic.
16+
*/
17+
public abstract boolean isDynamic();
18+
19+
public abstract boolean equals(Object obj);
20+
21+
/**
22+
* Precompute the byte size of the static ABI typed value
23+
* @return the byte size of the ABI value
24+
* @throws IllegalArgumentException if the ABI type is dynamic typed
25+
*/
26+
public abstract int byteLen();
27+
28+
/**
29+
* Encode JAVA values with ABI rules based on the ABI type schemes
30+
* @param obj JAVA values
31+
* @return byte[] of ABI encoding
32+
* @throws IllegalArgumentException if encoder cannot infer the type from obj
33+
*/
34+
public abstract byte[] encode(Object obj);
35+
36+
/**
37+
* Decode ABI encoded byte array to JAVA values from ABI type schemes
38+
* @param encoded byte array of ABI encoding
39+
* @return JAVA object of corresponding values
40+
* @throws IllegalArgumentException if encoded byte array cannot match with ABI encoding rules
41+
*/
42+
public abstract Object decode(byte[] encoded);
43+
44+
/**
45+
* Deserialize ABI type scheme from string
46+
* @param str string representation of ABI type schemes
47+
* @return ABI type scheme object
48+
* @throws IllegalArgumentException if ABI type serialization strings are corrupted
49+
*/
50+
public static ABIType valueOf(String str) {
51+
if (str.endsWith("[]")) {
52+
ABIType elemType = ABIType.valueOf(str.substring(0, str.length() - 2));
53+
return new TypeArrayDynamic(elemType);
54+
} else if (str.endsWith("]")) {
55+
Matcher m = staticArrayPattern.matcher(str);
56+
if (!m.matches())
57+
throw new IllegalArgumentException("static array type ill format: " + str);
58+
ABIType elemT = ABIType.valueOf(m.group("elemT"));
59+
int length = Integer.parseInt(m.group("len"));
60+
return new TypeArrayStatic(elemT, length);
61+
} else if (str.startsWith("uint")) {
62+
int size = Integer.parseInt(str.substring(4));
63+
return new TypeUint(size);
64+
} else if (str.equals("byte")) {
65+
return new TypeByte();
66+
} else if (str.startsWith("ufixed")) {
67+
Matcher m = ufixedPattern.matcher(str);
68+
if (!m.matches())
69+
throw new IllegalArgumentException("ufixed type ill format: " + str);
70+
int size = Integer.parseInt(m.group("size"));
71+
int precision = Integer.parseInt(m.group("precision"));
72+
return new TypeUfixed(size, precision);
73+
} else if (str.equals("bool")) {
74+
return new TypeBool();
75+
} else if (str.equals("address")) {
76+
return new TypeAddress();
77+
} else if (str.equals("string")) {
78+
return new TypeString();
79+
} else if (str.length() >= 2 && str.charAt(0) == '(' && str.endsWith(")")) {
80+
List<String> tupleContent = parseTupleContent(str.substring(1, str.length() - 1));
81+
List<ABIType> tupleTypes = new ArrayList<>();
82+
for (String subStr : tupleContent)
83+
tupleTypes.add(ABIType.valueOf(subStr));
84+
return new TypeTuple(tupleTypes);
85+
} else {
86+
throw new IllegalArgumentException("Cannot infer type from the string: " + str);
87+
}
88+
}
89+
90+
private static class Segment {
91+
public int L, R;
92+
93+
Segment(int left, int right) {
94+
this.L = left;
95+
this.R = right;
96+
}
97+
}
98+
99+
public static List<String> parseTupleContent(String str) {
100+
if (str.length() == 0)
101+
return new ArrayList<>();
102+
103+
if (str.startsWith(",") || str.endsWith(","))
104+
throw new IllegalArgumentException("parsing error: tuple content should not start with comma");
105+
106+
if (str.contains(",,"))
107+
throw new IllegalArgumentException("parsing error: tuple content should not have consecutive commas");
108+
109+
ArrayDeque<Integer> parenStack = new ArrayDeque<>();
110+
List<Segment> parenSegments = new ArrayList<>();
111+
112+
for (int i = 0; i < str.length(); i++) {
113+
if (str.charAt(i) == '(')
114+
parenStack.push(i);
115+
else if (str.charAt(i) == ')') {
116+
if (parenStack.isEmpty())
117+
throw new IllegalArgumentException("parsing error: tuple parentheses are not balanced: " + str);
118+
int leftParenIndex = parenStack.pop();
119+
if (parenStack.isEmpty())
120+
parenSegments.add(new Segment(leftParenIndex, i));
121+
}
122+
}
123+
if (!parenStack.isEmpty())
124+
throw new IllegalArgumentException("parsing error: tuple parentheses are not balanced: " + str);
125+
126+
String strCopied = str;
127+
for (int i = parenSegments.size() - 1; i >= 0; i--)
128+
strCopied = strCopied.substring(0, parenSegments.get(i).L) + strCopied.substring(parenSegments.get(i).R + 1);
129+
130+
String[] tupleSeg = strCopied.split(",", -1);
131+
int parenSegCount = 0;
132+
for (int i = 0; i < tupleSeg.length; i++) {
133+
if (tupleSeg[i].isEmpty()) {
134+
tupleSeg[i] = str.substring(parenSegments.get(parenSegCount).L, parenSegments.get(parenSegCount).R + 1);
135+
parenSegCount++;
136+
}
137+
}
138+
139+
return Arrays.asList(tupleSeg);
140+
}
141+
142+
protected static int findBoolLR(ABIType[] typeArray, int index, int delta) {
143+
int until = 0;
144+
while (true) {
145+
int currentIndex = index + delta * until;
146+
if (typeArray[currentIndex] instanceof TypeBool) {
147+
if (currentIndex != typeArray.length - 1 && delta > 0)
148+
until++;
149+
else if (currentIndex != 0 && delta < 0)
150+
until++;
151+
else
152+
break;
153+
} else {
154+
until--;
155+
break;
156+
}
157+
}
158+
return until;
159+
}
160+
161+
/**
162+
* Given an array/list-like object, infer an array of objects
163+
* @param val an array/list-like object
164+
* @return inferred array of objects
165+
* @throws IllegalArgumentException if it cannot infer if the object is list or array
166+
*/
167+
protected static Object[] unifyToArrayOfObjects(Object val) {
168+
if (val.getClass().isArray()) {
169+
if (val instanceof Object[])
170+
return (Object[]) val;
171+
int length = Array.getLength(val);
172+
Object[] outputArray = new Object[length];
173+
for (int i = 0; i < length; ++i)
174+
outputArray[i] = Array.get(val, i);
175+
return outputArray;
176+
} else if (val instanceof List<?>)
177+
return ((List<?>) val).toArray(new Object[0]);
178+
else
179+
throw new IllegalArgumentException("cannot infer type for unify array/list-like object to object array");
180+
}
181+
182+
/**
183+
* Take the first 2 bytes in the byte array
184+
* (consider the byte array to be an encoding for ABI dynamic typed value)
185+
* @param encoded an ABI encoding byte array
186+
* @return the first 2 bytes of the ABI encoding byte array
187+
* @throws IllegalArgumentException if the encoded byte array has length < 2
188+
*/
189+
protected static byte[] getLengthEncoded(byte[] encoded) {
190+
if (encoded.length < ABI_DYNAMIC_HEAD_BYTE_LEN)
191+
throw new IllegalArgumentException("encode byte size too small, less than 2 bytes");
192+
byte[] encodedLength = new byte[ABI_DYNAMIC_HEAD_BYTE_LEN];
193+
System.arraycopy(encoded, 0, encodedLength, 0, ABI_DYNAMIC_HEAD_BYTE_LEN);
194+
return encodedLength;
195+
}
196+
197+
/**
198+
* Take the bytes after the first 2 bytes in the byte array
199+
* (consider the byte array to be an encoding for ABI dynamic typed value)
200+
* @param encoded an ABI encoding byte array
201+
* @return the tailing bytes after the first 2 bytes of the ABI encoding byte array
202+
* @throws IllegalArgumentException if the encoded byte array has length < 2
203+
*/
204+
protected static byte[] getContentEncoded(byte[] encoded) {
205+
if (encoded.length < ABI_DYNAMIC_HEAD_BYTE_LEN)
206+
throw new IllegalArgumentException("encode byte size too small, less than 2 bytes");
207+
byte[] encodedString = new byte[encoded.length - ABI_DYNAMIC_HEAD_BYTE_LEN];
208+
System.arraycopy(encoded, ABI_DYNAMIC_HEAD_BYTE_LEN, encodedString, 0, encoded.length - ABI_DYNAMIC_HEAD_BYTE_LEN);
209+
return encodedString;
210+
}
211+
212+
/**
213+
* Cast a dynamic/static array to ABI tuple type
214+
* @param size length of the ABI array
215+
* @param t ABI type of the element of the ABI array
216+
* @return a type-cast from ABI array to an ABI tuple type
217+
*/
218+
protected static TypeTuple castToTupleType(int size, ABIType t) {
219+
List<ABIType> tupleTypes = new ArrayList<>();
220+
for (int i = 0; i < size; i++)
221+
tupleTypes.add(t);
222+
return new TypeTuple(tupleTypes);
223+
}
224+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.algorand.algosdk.abi;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonIgnore;
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Objects;
11+
12+
@JsonInclude(JsonInclude.Include.NON_NULL)
13+
public class Contract {
14+
@JsonProperty("name")
15+
public String name;
16+
@JsonProperty("appId")
17+
public Integer appId;
18+
@JsonProperty("methods")
19+
public List<Method> methods = new ArrayList<>();
20+
21+
@JsonCreator
22+
public Contract(
23+
@JsonProperty("name") String name,
24+
@JsonProperty("appId") Integer appId,
25+
@JsonProperty("methods") List<Method> methods
26+
) {
27+
this.name = Objects.requireNonNull(name, "name must not be null");
28+
this.appId = Objects.requireNonNull(appId, "name must not be null");
29+
if (methods != null) this.methods = methods;
30+
}
31+
32+
@JsonIgnore
33+
public String getName() {
34+
return this.name;
35+
}
36+
37+
@JsonIgnore
38+
public Integer getAppId() {
39+
return this.appId;
40+
}
41+
42+
@JsonIgnore
43+
public Method getMethodByIndex(int index) {
44+
return this.methods.get(index);
45+
}
46+
47+
@Override
48+
public boolean equals(Object o) {
49+
if (o == null || getClass() != o.getClass()) return false;
50+
Contract contract = (Contract) o;
51+
return Objects.equals(name, contract.name) && Objects.equals(appId, contract.appId) && Objects.equals(methods, contract.methods);
52+
}
53+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.algorand.algosdk.abi;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonIgnore;
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Objects;
11+
12+
@JsonInclude(JsonInclude.Include.NON_NULL)
13+
public class Interface {
14+
@JsonProperty("name")
15+
public String name;
16+
@JsonProperty("methods")
17+
public List<Method> methods = new ArrayList<>();
18+
19+
@JsonCreator
20+
public Interface(
21+
@JsonProperty("name") String name,
22+
@JsonProperty("methods") List<Method> methods
23+
) {
24+
this.name = Objects.requireNonNull(name, "name must not be null");
25+
if (methods != null) this.methods = methods;
26+
}
27+
28+
@JsonIgnore
29+
public String getName() {
30+
return this.name;
31+
}
32+
33+
@JsonIgnore
34+
public Method getMethodByIndex(int index) {
35+
return this.methods.get(index);
36+
}
37+
38+
@Override
39+
public boolean equals(Object o) {
40+
if (o == null || getClass() != o.getClass()) return false;
41+
Interface that = (Interface) o;
42+
return Objects.equals(name, that.name) && Objects.equals(methods, that.methods);
43+
}
44+
}

0 commit comments

Comments
 (0)