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

Enhancement: Deprecating use of langspec #367

Merged
merged 24 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
28352ce
deprecate stuffs, and prototype sanity check
ahangsu Aug 18, 2022
8aabe41
temp comment out version check
ahangsu Aug 19, 2022
4637f0b
remove ascii printable, for it might be false negative
ahangsu Aug 22, 2022
2ad3323
remove unnecessary deprecated commented lines
ahangsu Aug 22, 2022
bb8c247
skip test on checking `LogicsigSignature` invalid version in creation
ahangsu Aug 22, 2022
2a82ba3
deprecation message in check/read-Program
ahangsu Aug 22, 2022
94ae4cb
Update src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java
ahangsu Aug 22, 2022
5d6a589
Update src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java
ahangsu Aug 22, 2022
c69961a
Merge branch 'develop' into deprecate-langspec
ahangsu Aug 22, 2022
e7e98fc
remove tests that are against behaviors
ahangsu Aug 22, 2022
23f16bc
add doc string to `sanityCheckProgram`, rerun CI?
ahangsu Aug 22, 2022
0427e98
detailed deprecation msg
ahangsu Aug 22, 2022
8a91825
local testscript
ahangsu Aug 22, 2022
7551d32
update ascii printable back
ahangsu Aug 23, 2022
53bba55
cucumber test
ahangsu Aug 23, 2022
cfb6f24
Merge branch 'develop' into deprecate-langspec
ahangsu Aug 23, 2022
350460d
add empty program check
ahangsu Aug 24, 2022
2e8ee66
deprecation msg modify
ahangsu Aug 25, 2022
7cae4f5
more specific error checking santence
ahangsu Aug 25, 2022
0edecf8
move sanity check prog into logicsig def
ahangsu Aug 25, 2022
e331a98
Update .test-env
ahangsu Aug 25, 2022
8e402df
minor, remove 0xFF conversion in byte to char
ahangsu Aug 26, 2022
20f7f1a
Merge branch 'deprecate-langspec' of github.com:algorand/java-algoran…
ahangsu Aug 26, 2022
424db26
no conversion between byte and char
ahangsu Aug 26, 2022
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
69 changes: 57 additions & 12 deletions src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -39,6 +40,60 @@ public class LogicsigSignature {
@JsonProperty("msig")
public MultisigSignature msig;


private static boolean isAsciiPrintable(final byte symbol) {
char symbolChar = (char) (symbol & 0xFF);
ahangsu marked this conversation as resolved.
Show resolved Hide resolved
// linebreak existence check in program byte
boolean isBreakLine = symbolChar == '\n';
// printable ascii between range 32 (space) and 126 (tilde ~)
boolean isStdPrintable = symbolChar >= ' ' && symbolChar <= '~';
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,
Expand All @@ -48,14 +103,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;
Expand Down Expand Up @@ -124,11 +173,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 {
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/com/algorand/algosdk/logic/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -164,6 +168,7 @@ public static boolean checkProgram(byte[] program, List<byte[]> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
1 change: 1 addition & 0 deletions src/test/unit.tags
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@unit.indexer.logs
@unit.indexer.rekey
@unit.offline
@unit.program_sanity_check
@unit.rekey
@unit.responses
@unit.responses.231
Expand Down